首页 > 其他分享 >wsl,64位机,编译共享库

wsl,64位机,编译共享库

时间:2023-04-09 19:32:20浏览次数:36  
标签:addr wsl a1 a2 64 func printf so 位机


难得的周日,看了些动态链接的文章。 IOT物联网小镇 自己也想编译一个动态库,中间遇到了一些问题。写篇文章记录下。

b.c 代码

#include <stdio.h>

int b = 30;

void func_b(void)
{
    printf("in func_b. b = %d \n", b);
}

a.c代码如下. a.c依赖b.c

#include <stdio.h>

// 内部定义【静态】全局变量
static int a1 = 10;

// 内部定义【非静态】全局变量
int a2 = 20;

// 声明外部变量
extern int b;

// 声明外部函数
extern void func_b(void);

// 内部定义的【静态】函数
static void func_a2(void)
{
    printf("in func_a2 \n");
}

// 内部定义的【非静态】函数
void func_a3(void)
{
    printf("in func_a3 \n");
}

// 被 main 调用
void func_a1(void)
{
    printf("in func_a1 \n");

    // 操作内部变量
    a1 = 11;
    a2 = 21;

    // 操作外部变量
    b  = 31;

    // 调用内部函数
    func_a2();
    func_a3();

    // 调用外部函数
    func_b();
}

main.c代码如下 main依赖a.c

#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

// 声明外部变量
extern int a2;
extern void func_a1();

typedef void (*pfunc)(void);

int main(void)
{
    printf("in main \n");

    // 打印此进程的全局符号表
    void *handle = dlopen(0, RTLD_NOW);
    if (NULL == handle)
    {
        printf("dlopen failed! \n");
        return -1;
    }

    printf("\n------------ main ---------------\n");
    // 打印 main 中变量符号的地址
    pfunc addr_main = dlsym(handle, "main");
    if (NULL != addr_main)
        printf("addr_main = 0x%x \n", (unsigned int)addr_main);
    else
        printf("get address of main failed! \n");

    printf("\n------------ liba.so ---------------\n");
    // 打印 liba.so 中变量符号的地址
    unsigned int *addr_a1 = dlsym(handle, "a1");
    if (NULL != addr_a1)
        printf("addr_a1 = 0x%x \n", addr_a1);
    else
        printf("get address of a1 failed! \n");

    unsigned int *addr_a2 = dlsym(handle, "a2");
    if (NULL != addr_a2)
        printf("addr_a2 = 0x%x \n", addr_a2);
    else
        printf("get address of a2 failed! \n");

    // 打印 liba.so 中函数符号的地址
    pfunc addr_func_a1 = dlsym(handle, "func_a1");
    if (NULL != addr_func_a1)
        printf("addr_func_a1 = 0x%x \n", (unsigned int)addr_func_a1);
    else
        printf("get address of func_a1 failed! \n");

    pfunc addr_func_a2 = dlsym(handle, "func_a2");
    if (NULL != addr_func_a2)
        printf("addr_func_a2 = 0x%x \n", (unsigned int)addr_func_a2);
    else
        printf("get address of func_a2 failed! \n");

    pfunc addr_func_a3 = dlsym(handle, "func_a3");
    if (NULL != addr_func_a3)
        printf("addr_func_a3 = 0x%x \n", (unsigned int)addr_func_a3);
    else
        printf("get address of func_a3 failed! \n");


    printf("\n------------ libb.so ---------------\n");
    // 打印 libb.so 中变量符号的地址
    unsigned int *addr_b = dlsym(handle, "b");
    if (NULL != addr_b)
        printf("addr_b = 0x%x \n", addr_b);
    else
        printf("get address of b failed! \n");

    // 打印 libb.so 中函数符号的地址
    pfunc addr_func_b = dlsym(handle, "func_b");
    if (NULL != addr_func_b)
        printf("addr_func_b = 0x%x \n", (unsigned int)addr_func_b);
    else
        printf("get address of func_b failed! \n");

    dlclose(handle);

    // 操作外部变量
    a2 = 100;

    // 调用外部函数
    func_a1();

    // 为了让进程不退出,方便查看虚拟空间中的地址信息
    while(1) sleep(5);
    return 0;
}
gcc b.c -fPIC -shared -o libb.so -m64
gcc a.c -fPIC -shared -o liba.so -l b -L. -m64
gcc main.c  -L. -l a -l b -o main -m64 -ldl    -- ldl一定要加上


选项	说明
-L	    指明共享库所在的目录
-l	    指明共享库的名称,该名称是处在头lib 和后缀.so 中的名称。例如上面共享库libb.so,则参数为 -l b 。
查看应用程序依赖的共享库: ldd libb.so
查看目标文件中定义的符号: nm libb.so

遇到问题一:

gcc编译报错如下

wsl,64位机,编译共享库_重定位


解决方案: 需要在编译时加上-ldl。

遇到问题二:

编译a.c后, ldd liba.so, 发现libb.so => not found.

wsl,64位机,编译共享库_开发语言_02


解决方案: 设置LD_LIBRARY_PATH.

参考此方案 export LD_LIBRARY_PATH 的使用

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

一切都弄好好,重新ldd liba.so,ldd main. 一切都正常。

wsl,64位机,编译共享库_开发语言_03

执行main

wsl,64位机,编译共享库_c语言_04

通过 ps -ef | grep main 获取进程id.

wsl,64位机,编译共享库_#include_05


通过指令:$ cat /proc/[进程的 pid]/maps 读取程序的虚拟内存区域。

wsl,64位机,编译共享库_c语言_06

readelf -s liba.so 查看符号表.
动态链接库中保护两个符号表:.dynsym(动态符号表: 表示模块中符号的导出、导入关系) 和 .symtab(符号表: 表示模块中的所有符号);
.symtab 中包含了 .dynsym;

红色矩形框前面的Ndx列是数字,表示该符号位于当前文件的哪一个段中(即:段索引);

绿色矩形框前面的Ndx列是UND,表示这个符号没有找到,是一个外部符号(需要重定位);

wsl,64位机,编译共享库_c++_07


wsl,64位机,编译共享库_重定位_08

通过readelf -S liba.so指令来看一下这个ELF文件中都有哪些section:

wsl,64位机,编译共享库_c++_09


wsl,64位机,编译共享库_c++_10


可以看到:一共有29个section,其中的22、23就是两个GOT表。通过指令 readelf -l liba.so ,来查看一下segment信息:装载器并不是把这些sections分开来处理,而是根据不同的读写属性,把多个section看做一个segment。

wsl,64位机,编译共享库_c语言_11

wsl,64位机,编译共享库_c++_12

那么:liba.so通过什么方式来告诉动态链接器:需要对.got和.got.plt这两个表中的表项进行地址重定位呢?

在静态链接的时候,目标文件是通过两个重定位表.rel.text和.rel.data这两个段信息来告诉链接器的。

对于动态链接来说,也是通过两个重定位表来传递需要重定位的符号信息的,只不过名字有些不同:.rela.dyn和.rela.plt。

通过指令 readelf -r liba.so来查看重定位信息:

wsl,64位机,编译共享库_c++_13


从上图可以看出:

liba.so 引用了外部符号 b,类型是 R_386_GLOB_DAT,这个符号的重定位描述信息在 .rela.dyn 段中;

liba.so 引用了外部符号 func_b, 类型是 R_386_JUMP_SLOT,这个符号的重定位描述信息在 .rela.plt 段中;

如下图所以,该load段的虚拟内存地址为0x0000000000003df0. 因此我们可以计算出.got, .got.plt的虚拟内存地址。

wsl,64位机,编译共享库_c++_14


wsl,64位机,编译共享库_c++_15

objdump -d liba.so 反汇编liba.so代码。 这里贴出func_a1函数的反汇编代码:

wsl,64位机,编译共享库_c++_16


标签:addr,wsl,a1,a2,64,func,printf,so,位机
From: https://blog.51cto.com/u_6760421/6178929

相关文章

  • 练习记录-cf-div2-864(A-D)
    状态不怎么好场上就写出3道还磨磨蹭蹭推错结论qwq 警钟长鸣A.LiHuaandMaze一开始以为要切割发现就把其中一个包起来就行了计算包某个块需要的最小块数#include<bits/stdc++.h>#defineclosestd::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)usingn......
  • 【cf864】赛后结
    属实是失踪人口了,想了一下还是把题解打到这儿。conteset地址:https://codeforces.com/contest/1797 A.题目大意:n*m的方格上给两个点,询问最少增加的障碍格子使得这两个点不连通。解题思路:水题,但是手速有点慢。直接问靠不靠墙,靠几面墙,不靠墙答案4,靠一面答案3,靠两面答案2,取两个点......
  • Codeforces Round 864 (Div. 2)
    评价:A~E感觉出的挺一般,特别是D怎么放这种暴力题,场上我还没调出来,F没看。但是Orzrui_er。A在一个点周围放障碍即可。B求出最少需要的操作次数\(p\),若\(p>k\)就无解,否则若\(n\)为偶数只能任选一个格子翻偶数次,即有解当且仅当\(2\mid(k-p)\);若\(n\)为奇数可......
  • OS-Linux-Ubuntu22.04x64-Python-C++调用Python缺少Python.h
    OS-Linux-Ubuntu22.04x64-Python-C++调用Python缺少Python.h使用C或C++扩展Python扩展和嵌入Python解释器Python3.10.11Python/CAPI参考手册Python3.11.3Python/CAPI参考手册参考https://www.cnblogs.com/lidabo/p/17043302.htmlhttps://blog.csdn.net/z......
  • delphi中Base64编码转成PDF文件
    Base64编码转成PDF文件  PDF文件转成Base64编码:首先,将PDF文件加载到MemoryStream中:varms:TMemoryStream;beginms:=TMemoryStream.Create;tryms.LoadFromFile('file.pdf');然后,使用TIdEncoderMIME将TMemoryStream转换为Base64编码的字符串:varencoder:TIdEncoderMIME;base......
  • WSL使用教程
    1.下载WSL环境配置ubuntu安装包VCS工具安装包2.准备1)在管理员模式下打开PowerShell(强烈建议安装terminal操作方便界面效果好)dism.exe/online/enable-feature/featurename:Microsoft-Windows-Subsystem-Linux/all/norestart#启用虚拟化dism.exe/online/......
  • C#.NET 国密 BASE64编码的私钥提取16进制私钥
    C#.NET国密BASE64编码的私钥提取16进制私钥,从BASE64编码的公钥中提取16进制字符串公钥, 从BASE64编码的私钥中提取16进制字符串私钥, 锦州银行在使用这种私钥。 StringmchtPubKey="MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAElmWpvTHHsQEUMSLoMcDssXAjCkdgjCkncPXNnnapIEk......
  • BRUP使用技巧——对BASE64编码的密码进行爆破
    一、对某系统进行安全测试,检查登录页面的安全性可以看到用户名、密码等信息以POST的方式进行提交,POST内容示例如下:{"sign":"encodebybase64","ts":123456,"loginType":"xx"}二、对BASE64进行解码通过解码可以看到其格式为{"username":"admin","password":&qu......
  • C, cython和pandas dataframe交互int64, int32的选择
    cython调用C代码的一个错误expected'int'butgot'long',原因不复杂,Ccode的int为32bit,而pandasdf缺省为np.int64(64bit),有个参数传递了数组,指针类型就不符了。两个解决方案C代码里面所有相关的int改为longlong类型或者使用pandasdataframe前转换为np.int32,即df.as......
  • 图片转二进制 base64
    functiongetBase64Image(img){varcanvas=document.createElement("canvas");canvas.width=img.width;canvas.height=img.height;varctx=canvas.getContext("2d");......