动态库函数 printf研究

本文详细介绍了ARM汇编语言中如何使用特定指令进行调试的过程,包括加载寄存器、调用函数、修改内存等操作,并展示了如何通过跳转指令调用C语言标准库中的printf函数实现打印调试信息。




ENTRY(my_tgkill)
    stmdb  sp!, {r4-r11, ip, lr} 
    //ldr r0, debug_message
    //mov r1, pc
    //bl dp_printf


    ldr r4, debug_message
    mov r0, r4
    mov r1, pc 
    nop
    nop
    nop
    bl printf             
    nop
    nop
    nop
    nop
    nop
    add r4,r4,#20
    mov r0, r4
    str r0, [r0]
    ldr r0, func_point123
    nop
    add ip, pc, #0, 12
    nop
    nop
    nop
    nop
    
    add ip, ip, #8192
    ldr r1, [ip, #1896]
     
/*
00000654 <printf@plt>:
654: e28fc600 add ip, pc, #0, 12
658: e28cca02 add ip, ip, #8192 ; 0x2000
65c: e5bcf954 ldr pc, [ip, #2388]! ; 0x954
*/
    nop
    nop
    nop
    nop
    nop
    ldr r0, debug_message
//bl printf

blx r1

//r1为printf的地址 在got表中 实际中是跳转到00000654 <printf@plt>:
 654: e28fc600 add ip, pc, #0, 12
 658: e28cca02 add ip, ip, #8192 ; 0x2000
 65c: e5bcf954 ldr pc, [ip, #2388]! ; 0x954

,从got表中获取printf的实际地址,我计算了654与本程序pc的偏移,然后直接算出got表中printf

的地址

blx r1调用之,由于printf是thrumb指令,用blx


//add lr,  pc
//movs pc, r1
    //add ip, ip, #4096
    //ldr pc, [ip, #2444]!
    
    //mov  r1,ip  
    //bl dp_printf




// 59c: e28fc600 .word 0xe28fc600
// 5a0: e28cca01 .word 0xe28cca01
// 5a4: e5bcfa58 .word 0xe5bcfa58


    //ldr r3, printf
    //ldr pc,printf   
    //add ip, pc, #0, 12
    //add ip, ip, #4096
    //;0x1000
    //ldr pc, [ip, #2656]!
    //; 0xa60


 



    ldmia  sp!, {r4-r11, ip, pc}
                           
    error_code: .word  0x1ffc
    func_point123: .word  printf
    debug_message: .word  debug_string
                      
    
END(my_tgkill)






ENTRY(dp_printf)
    stmdb  sp!, {lr}
    bl printf
    ldmia  sp!, {pc}
END(dp_printf)






.data
debug_string:
.string "valuewww = 0x%x.\n"
.space 0x400, 0
   .end



调用动态库函数主要分为静态调用和动态调用两种方法: #### 静态调用 在程序编译时,通过编译器的相关命令来指定要链接的动态库。例如,程序 `test` 启动时需要加载目录 `/root/src/lib` 中的 `libtest_so1.so` 动态库,编译命令可如下编写执行:在编译时使用 `g++` 的 `-L` 和 `-l` 命令来指定动态库的路径和名称。示例命令: ```bash g++ -o test test.cpp -L/root/src/lib -ltest_so1 ``` 在日常开发中,只要配置好项目属性,程序运行时系统会自动加载项目所引用的动态库文件,程序员无需关心如何加载[^2][^3]。 #### 动态调用 以 C 语言为例,借助 `dlopen` 和 `dlsym` 等函数在运行时动态加载和调用动态库中的函数。示例代码如下: ```c #include <dlfcn.h> #include <stdio.h> int main(void) { void *phandle = NULL; void *perr = NULL; int (*pstrfunction)(char *str); char *str = "hello world!"; // 打开动态库 phandle = dlopen("./libstr.so", RTLD_LAZY); if (!phandle) { printf("dlopen error \n"); } // 检查是否有错误 perr = dlerror(); if (perr != NULL) { printf(" error %s \n", perr); return -1; } // 获取函数指针 pstrfunction = dlsym(phandle, "my_strlen"); perr = dlerror(); if (perr != NULL) { printf(" error %s \n", perr); return -1; } // 调用函数 int len = pstrfunction(str); printf(" the str len = [%d] \n", len); // 关闭动态库 dlclose(phandle); return 0; } ``` 在上述代码中,`dlopen` 用于打开动态库,将其装载到内存;`dlsym` 用于获得指定函数在内存中的位置(指针);`dlerror` 用于检查是否有错误发生;`dlclose` 用于关闭动态库。使用这种方式,程序可以在不重新编译的情况下,添加或修改库中的函数,同时也为程序提供了更大的灵活性。并且由于只有当程序实际需要时才加载库,因此可以节省内存。如果多个程序共享同一个库,那么这个库只需要在内存中加载一次。还可以更容易地控制库的版本[^1][^5]。 在 Python 中也可以动态调用函数,例如使用内置库 `operator` 中的 `attrgetter` 方法,或者使用 `getattr` 函数。示例代码如下: ```python from operator import attrgetter class People: def speak(self, dest): print("Hello, %s" % dest) p = People() # 使用 attrgetter caller = attrgetter("speak") caller(p)("Tony") # 使用 getattr caller2 = getattr(p, "speak") caller2("Tony") ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值