本篇文章主要介绍了"Android C语言_init函数和constructor属性及.init/.init_array节探索",主要涉及到Android C语言_init函数和constructor属性及.init/.init_array节探索方面的内容,对于Android C语言_init函数和constructor属性及.init/.init_array节探索感兴趣的同学可以参考一下。

了解C语言的程序猿都知道有两种方法可以让一部分代码在so或可执行文件被加载的时候先于其它任何函数执行,一种是定义一个void _init(void)函数,另一种是在函数后面声明constructor属性。那么这两种方式在执行的时候有什么区别吗?先后顺序呢?了解ELF文件格式的人又会问它们在文件中的位置又有什么差别呢?这篇文章就来解答这些问题。

首先你需要了解一下ELF文件格式了,这里就不啰嗦了,不了解的人可以搜一下看看。

下面是一个例子,在你的Android工程中的C/C++代码中加入下面几行:

  1. ........
  2.  
  3. #ifdef __cplusplus
  4. extern "C" {
  5. #endif
  6.  
  7. void _init(void){mlog_info("_init enter");}
  8.  
  9. #ifdef __cplusplus
  10. }
  11. #endif
  12.  
  13. void __attribute__((constructor)) myConstructor(void){mlog_info("myConstructor enter\n");}
  14.  
  15. ........

我这边编译出来是libcheckcert.so文件,放到手机上去运行的结果是:

  1. ........
  2.  
  3. 12-13 11:04:46.603: I/BRIAN(12203): _init enter
  4. 12-13 11:04:46.603: I/BRIAN(12203): myConstructor enter
  5.  
  6. ........

_init函数是最先运行的,为什么会这样呢?了解ELF文件的人都知道有.init和.init_array这两个节,它们是ELF文件在加载的时候用来做初始化的,那么它们和_init函数及constructor属性有什么关系呢?下面我们需要借助readelf和IDA pro来查看,首先readelf -d libcheckcert.so来查看ELF的dynamic段:

  1. BriansdeMacBook-Pro:armeabi-v7a brian$ arm-linux-androideabi-readelf -d libcheckcert.so
  2.  
  3. Dynamic section at offset 0x19b80 contains 27 entries:
  4. Tag Type Name/Value
  5. 0x00000003 (PLTGOT) 0x1ad84
  6. 0x00000002 (PLTRELSZ) 1248 (bytes)
  7. 0x00000017 (JMPREL) 0x4200
  8. 0x00000014 (PLTREL) REL
  9. 0x00000011 (REL) 0x31a8
  10. 0x00000012 (RELSZ) 4184 (bytes)
  11. 0x00000013 (RELENT) 8 (bytes)
  12. 0x6ffffffa (RELCOUNT) 390
  13. 0x00000006 (SYMTAB) 0x148
  14. 0x0000000b (SYMENT) 16 (bytes)
  15. 0x00000005 (STRTAB) 0x1028
  16. 0x0000000a (STRSZ) 6825 (bytes)
  17. 0x00000004 (HASH) 0x2ad4
  18. 0x00000001 (NEEDED) Shared library: [liblog.so]
  19. 0x00000001 (NEEDED) Shared library: [libc.so]
  20. 0x00000001 (NEEDED) Shared library: [libm.so]
  21. 0x00000001 (NEEDED) Shared library: [libstdc++.so]
  22. 0x00000001 (NEEDED) Shared library: [libdl.so]
  23. 0x0000000e (SONAME) Library soname: [libcheckcert.so]
  24. 0x0000000c (INIT) 0x4f9c
  25. 0x0000001a (FINI_ARRAY) 0x1a658
  26. 0x0000001c (FINI_ARRAYSZ) 8 (bytes)
  27. 0x00000019 (INIT_ARRAY) 0x1a660
  28. 0x0000001b (INIT_ARRAYSZ) 20 (bytes)
  29. 0x0000001e (FLAGS) BIND_NOW
  30. 0x6ffffffb (FLAGS_1) Flags: NOW
  31. 0x00000000 (NULL) 0x0

可以看到INIT和INIT_ARRAY节的地址分别为0x4f9c和0x1a660,打开IDA pro来查看相应位置的代码:

  1. .text:00004F9C ; =============== S U B R O U T I N E =======================================
  2. .text:00004F9C
  3. .text:00004F9C ; Attributes: bp-based frame
  4. .text:00004F9C
  5. .text:00004F9C EXPORT _init
  6. .text:00004F9C _init
  7. .text:00004F9C
  8. .text:00004F9C var_8 = -8
  9. .text:00004F9C var_4 = -4
  10. .text:00004F9C
  11. .text:00004F9C STMFD SP!, {R11,LR}
  12. .text:00004FA0 MOV R11, SP
  13. .text:00004FA4 SUB SP, SP, #8
  14. .text:00004FA8 LDR R0, =(_GLOBAL_OFFSET_TABLE_ - 0x4FB4)
  15. .text:00004FAC ADD R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
  16. .text:00004FB0 MOV R1, #4
  17. .text:00004FB4 LDR R2, =(aBrian_1 - 0x1AD84)
  18. .text:00004FB8 ADD R2, R2, R0 ; "BRIAN"
  19. .text:00004FBC LDR R3, =(a_initEnter - 0x1AD84)
  20. .text:00004FC0 ADD R0, R3, R0 ; "_init enter"
  21. .text:00004FC4 STR R0, [SP,#8+var_4]
  22. .text:00004FC8 MOV R0, R1
  23. .text:00004FCC MOV R1, R2
  24. .text:00004FD0 LDR R2, [SP,#8+var_4]
  25. .text:00004FD4 BL __android_log_print
  26. .text:00004FD8 STR R0, [SP,#8+var_8]
  27. .text:00004FDC MOV SP, R11
  28. .text:00004FE0 LDMFD SP!, {R11,PC}
  29. .text:00004FE0 ; End of function _init
  1. init_array:0001A660 ; ===========================================================================
  2. .init_array:0001A660
  3. .init_array:0001A660 ; Segment type: Pure data
  4. .init_array:0001A660 AREA .init_array, DATA
  5. .init_array:0001A660 ; ORG 0x1A660
  6. .init_array:0001A660 DCD _Z13myConstructorv ; myConstructor(void)
  7. .init_array:0001A664 DCD sub_4E90
  8. .init_array:0001A668 DCD sub_4EA8
  9. .init_array:0001A66C DCD sub_4F04
  10. .init_array:0001A670 DCB 0
  11. .init_array:0001A671 DCB 0
  12. .init_array:0001A672 DCB 0
  13. .init_array:0001A673 DCB 0
  14. .init_array:0001A673 ; .init_array ends

可以看到上面的代码中执行了我们所定义的函数,.init节就是_init函数的代码,而.init_array节是一个指针数组,每一项对应的是一块代码,可以做一系列的初始化操作。那么为什么.init节的代码先于.init_array节的代码执行呢?这个要看linker的代码了,位置在AOSP的bionic/linker目录下,这里只摘录里面的一小段代码:

  1. void soinfo::CallConstructors() {
  2.  
  3. ........
  4.  
  5. // DT_INIT should be called before DT_INIT_ARRAY if both are present.
  6. CallFunction("DT_INIT", init_func);
  7. CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);
  8. }

可以看到先执行.init节中的代码,然后在顺序执行.init_array中的各个代码块。

到这里大家应该对_init函数、constructor属性及.init节和.init_array节的对应情况了解的很清楚了吧。

下面说一个我不太清楚的地方,用readelf查看ELF中的所有符号信息,可以看到在.rel.dyn和.rel.plt都有myConstructor符号,一个类型是R_ARM_ABS32一个是R_ARM_JUMP_SLOT。另外在IDA pro中查看myConstructor可以发现它的代码主体是在.text节,但是也可以发现在.plt和.got节中也有myConstructor的定义。这样的话每次显式调用myConstructor的时候都需要通过PLT来跳转然后从GOT表中来找到myConstructor在TEXT节中的真正地址才能执行。但在.init_array中的地址是它在TEXT节中的真正地址,初始化的时候调用myConstructor并不需要通过PLT和GOT表。不明白这是为什么?留待以后解决吧。

更新:上面这个问题是因为编译器的问题,不同的编译器编译出来的ELF文件是不太一样的,上面我说的这种情况是LLVM编译器编译出来的,而如果用arm-linux-androideabi-*的话myConstructor符号是只有在.text节中才有,不会出现在.rel.dyn和.rel.plt中。

Android C语言_init函数和constructor属性及.init/.init_array节探索的更多相关文章

  1. android studio没有浮现函数用法和属性说明?

    最近转用android studio,在使用eclipse和android studio时原本在鼠标停留处或智能提示能浮现文档相关内容,但我的是一直显示Fetching Documentation…… ...

  2. cocos2d-x 通过JNI实现c/c++和Android的java层函数互调

    文章摘要: 本文主要实现两个功能: (1)通过Android sdk的API得到应用程序的包名(PackageName),然后传递给c++层函数. (2)通过c++函数调用Android的java层函 ...

  3. JavaScript 构造函数 prototype属性和_proto_和原型链 constructor属性 apply(),call()和bind() 关键字this

    1.构造函数: 通常构造函数首字母需要大写,主要是为了区别ECMAScript的其它函数.(高程三 P145) 构造函数与其他函数的唯一区别,就在于调用它们的方式不同.只要通过new来调用,任何函数都 ...

  4. constructor属性解析

    JavaScript中constructor属性一直不是很清楚,今日终于弄清了其中缘由,下面举例说明. 首先是一个典型的JavaScript实例声明: function Person(){ this. ...

  5. js 对象的_proto_属性 和函数的prototype属性分析

    bill是 Employee类型的实例,_proto_指向Employee.prototype Employee.prototype有一个constructor属性,指向Employee函数自身 fu ...

  6. javascript对象constructor属性

    概述 返回一个指向创建了该对象原型的函数引用.需要注意的是,该属性的值是那个函数本身,而不是一个包含函数名称的字符串.对于原始值(如1,true 或 "test"),该属性为只读. ...

  7. 【R】R语言常用函数

    R语言常用函数 基本 一.数据管理vector:向量 numeric:数值型向量 logical:逻辑型向量character:字符型向量 list:列表 data.frame:数据框c:连接为向量或 ...

  8. javascript的constructor属性

    /* constructor 属性 constructor 属性返回所有 JavaScript 变量的构造函数. */console.log("John".constructor) ...

  9. Javascript的构造函数和constructor属性

    原型链 function Foo() { this.value = 42;}Foo.prototype = { method: function() {}}; function Bar() {} // ...

随机推荐

  1. myeclipse tomcat部署按钮点击没反应

    进入workspace目录,删除.metadata\.plugins\org.eclipse.core.runtime\.settings\com.genuitec.eclipse.ast.deplo ...

  2. angular-列表进行添加、编辑等操作时此行变色。

    今天接触了一个功能,就是在一个列表中,当你新增或者对第N列进行编辑,删除等操作时这一列会变颜色.让用户对操作了哪行数据更认识更清晰,刷新之后这行的颜色就会消失.我觉得这个很有意思,记录一下.效果如下. ...

  3. (Python爬虫05)完善的爬虫学习大纲

  4. 下拉网页div自动浮在顶部

    <!DOCTYPE html> <html> <head> <title></title> <style type="tex ...

  5. 十一:Centralized Cache Management in HDFS 集中缓存管理

    集中的HDFS缓存管理,该机制可以让用户缓存特定的hdfs路径,这些块缓存在堆外内存中.namenode指导datanode完成这个工作. Centralized cache management i ...

  6. 蓝牙ble数据转语音实现Android AudioRecord方法推荐

    蓝牙ble数据转语音实现Android AudioRecord方法推荐 教程  欢迎走进zozo的学习之旅. 概述 蓝牙BLE又称bluetooth smart,主打的是低功耗和快速链接,所以在支持的 ...

  7. Hadoop之block研究

        本文翻译原链接:https://hadoopabcd.wordpress.com/2015/03/17/hdfs-file-blocks-distribution-in-datanodes/ ...

  8. TCP系列22—重传—12、Forward Retransmit

    一.概述 forward retransmit相关的内容在RFC6675中有描述,可以参考RFC6675 section 4中NextSeg ()的定义.forward retransmit中文名可以 ...

  9. <Effective C++>读书摘要--Implementations<一>

    1.For the most part, coming up with appropriate definitions for your classes (and class templates) a ...

  10. 3ds Max学习日记(一)

      暑假闲来无事学习一发3ds Max.为啥要学这玩意?貌似可以用这东西三维建模.暑期生产实习选了一个搞vr的导师,貌似他忙得很,无奈只好先自己研究一下啦~   vr神马的还是有点意思的,虽然自己仅仅 ...