深入剖析Linux动态库在内存的装载

动态库热加载实战:从dlopen到插件化架构 动态库与静态库的编译链接差异,决定了程序能否在运行期间实现模块替换。热加载技术通过dlopen/dlsym/dlclose等系统调用,在进程不退出时完成代码更新,为不停机维护、插件生态和高频迭代提供基础。理解符号解析、ABI稳定性和生命周期管理,是避免运行时崩溃的关键。从ONNX Runtime推理引擎封装到CODESYS工控环境集成,动态库热加载的工程实践覆盖服务器、嵌入式与AI部署场景。掌握这些方法,能有效提升系统的可扩展性与运维效率。 阅读详情

这是之前写的一个总结,现在回过头来看还是有一些收获,可以自己写一个动态库来进行测试,本文是以我自己写的动态库来进行测试的,使用工具readelf。

 

.so文件是elf格式文件中的一种,它遵循elf格式的相关规则,在对so文件进行内存装载时我们先学习一下elf格式中的相关内容(这里只是做基础的介绍,较为详细的内容可以参考《ELF文件格式分析》,也可以查阅网络资料,不过大多数网络资料都是从这里面提取的)。

一、可执行链接格式

可执行链接格式(Executable and Linking Format)最初是由 UNIX 系统实验室(UNIX System Laboratories,USL)开发并发布的,作为应用程序二进制接口(Application Binary Interface,ABI)的一部分。

目标文件有三种类型:

  • 可重定位文件(Relocatable File) .o)包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。
  • 可执行文件(Executable File) .exe) 包含适合于执行的一个程序,此文件规定了exec() 如何创建一个程序的进程映像。
  • 共享目标文件(Shared Object File) .so) 包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理, 生成另外一个目标文件。其次动态链接器(Dynamic Linker)可能将它与某 个可执行文件以及其它共享目标一起组合,创建进程映像。

目标文件全部是程序的二进制表示,目的是直接在某种处理器上直接执行。

 

1、ELF文件格式

ELF文件格式排布如下图1所示,分为链接视图和执行视图,我们的so共享库文件对应着链接视图来进行分析即可

 

图1 ELF文件格式

从图1中可以看出链接视图主要分为ELF头部、程序头部表、节区以及节区头部表。这些内容的定义可以在文件目录中的elf.h中看到,这里不加赘述,过多赘述,直接进入主题,来了解动态库在内存中的装载(以libcac.so为例)。

二、确定装载内容

一个so文件中有这么多的内容格式,哪些才是需要被我们装载的内容呢,通过分析dlopen的源码。

https://blog.youkuaiyun.com/SweeNeil/article/details/83744843

https://blog.youkuaiyun.com/SweeNeil/article/details/83749186

在其中发现真正被加载进内存的是Program Header对应的内容,而且该内容在Program Header中对应的type类型还应该为:PT_LOAD

通过对我们编写的程序的运行(elfso.c)可以看到这部分如下图2所示,红色方框中标识的type为1即表示为type类型为PT_LOAD这是需要被加载进内存的。

 

图2 libcac.so程序头内容

同样的也可以使用readelf工具来进行相关内容的查看,使用readelf查看获得的内容如下图3所示,红色方框中的内容就是需要被加载到内存中的,他们的type类型都为LOAD,可以看到使用readelf工具查看得到的信息和我们自己写的程序得到的信息是一致的。

 

图3 readelf查看libcac.so中程序头的内容

在标志为PT_LOAD的程序头中,它包含了需要映射的内容在文件中的位置以及映射的相关内容(这些内容可以再elf.h文件中程序头的定义中得到)。

三、动态库在内存中的装载

1、加载所有需要加载到内存的内容

通过上述第二节,知道了需要被加载进内存的内容,那么要如何加载呢,首先分析dlopen中的加载进内存的方式。

部分代码如下:

struct loadcmd

{

     ElfW(Addr) mapstart, mapend, dataend, allocend;

     off_t mapoff;

     int prot;

} loadcmds[l->l_phnum], *c;

size_t nloadcmds = 0;

 

case PT_LOAD:

c = &loadcmds[nloadcmds++];

c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);

c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1));

c->dataend = ph->p_vaddr + ph->p_filesz;

c->allocend = ph->p_vaddr + ph->p_memsz;

c->mapoff = ph->p_offset & ~(GLRO(dl_pagesize) - 1);

if (nloadcmds > 1 && c[-1].mapend != c->mapstart)

has_holes = true;

         ……

maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;

l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,

                                     c->prot,

                                     MAP_COPY|MAP_FILE,

                                     fd, c->mapoff);

l->l_map_end = l->l_map_start + maplength;

l->l_addr = l->l_map_start - c->mapstart;

 

__mprotect ((caddr_t) (l->l_addr + c->mapend),

                         loadcmds[nloadcmds - 1].mapstart - c->mapend,

                         PROT_NONE);

l->l_map_start = c->mapstart + l->l_addr;

l->l_map_end = l->l_map_start + maplength;

l->l_contiguous = !has_holes;

结合实验所使用的libcac.so文件,通过第二节我们知道在libcac.so中需要装载进内存的段有两个,他们的信息如下图4所示,包括要映射的内容在文件中的偏移量,虚拟地址,文件大小等。

图4 要加载进内存的Segment的信息

 

通过图4的相关内容可以填充上述的loadcmds结构体(蓝色部分代码),由于这里需要装载进内存的Segment有两个,因此这里以C1表示第一个,C2表示第二个来进行计算。

C1->mapstart = 0 & ~(GLRO(dl_pagesize)-1)

C1->mapend = ((0+0x774+GLRO(dl_pagesize)-1)&~(GLRO(dl_pagesize)-1)

C1->dataend = 0+0x774

C1->allocend = 0+0x774

C1->mapoff = 0&~(GLRO(dl_pagesize)-1)

C2->mapstart = 0x200e28 & ~(GLRO(dl_pagesize)-1)

C2->mapend = ((0x200e28+0x1e8+GLRO(dl_pagesize)-1)&~(GLRO(dl_pagesize)-1)

C2->dataend = 0x200e28+0x1e8

C2->allocend = 0x200e28+0x1f8

C2->mapoff = 0xe28&~(GLRO(dl_pagesize)-1)

 

其中dl_pagesize为4096(根据自己电脑来确定,使用getconf PAGE_SIZE命令查看),如下图5所示,使用getconf PAGE_SIZE获得的pagesize为4096。

图5 获得pagesize

得到了pagesize之后,计算可知GLRO(dl_pagesize)-1等于0xfff,因此上述代码&上这样一个pagesize其实是为了将后12位清零,达到页对齐的目的(个人理解)。因此,下一步就可以将C1、C2中的具体内容计算出来。

C1->mapstart = 0

C1->mapend = 0x1000

C1->dataend = 0x774

C1->allocend = 0x774

C1->mapoff = 0

 

C2->mapstart = 0x200000

C2->mapend = 0x202000

C2->dataend = 0x201010

C2->allocend = 0x201020

C2->mapoff = 0

 

这里最开始一直不能理解,为什么mapoff也为0了,这里要将mapoff与mapstart结合起来看,他们都将不足一页的偏移变为了0,在读的时候,相应位置的实际内容是内有改变的,其实细心点可以发现,他们的偏移是一样的,比如虚拟地址为0x200e28,文件偏移为e28,mapoff变为0了,mapstart变为了0x200000,但是0x200e28这个地址的内容还是从文件偏移e28开始的。(个人理解)

 

了解到了C1、C2之中的内容,再来计算它的maplength是如何确定的,maplength的计算由绿色部分代码完成,即最后一个Segment的的allocend减去第一个Segment的mapstart(在第一部分映射中就讲所有空间确定,因此这里映射的标志不是MAP_FIXED)。

因此实验中所使用的libcac.so的maplength就为:

maplength = C2->allocend – C1->mapstart = 0x201020-0 = 0x201020 = 2101280

进一步推出(下面用到的C都是C1)

l->l_map_start = __mmap ((void *) mappref, 2101280,C1->prot,MAP_COPY|MAP_FILE,fd, 0);

l->l_map_end = l->l_map_start + 0x201020;

l->l_addr = l->l_map_start - 0;

__mprotect ((l->l_addr +0x1000),C2->mapstart – C1->mapend,PROT_NONE);

=》__mprotect((l->l_map_start+0x1000),0x1ff000,PROT_NONE);

 

这里的__mprotect把中间部分保护起来,禁止所有访问,就像未分配一样,然后跳转到正常的段映射循环。

2、装载各个Segment

最开始不太理解,为什么已经全部映射了,却还要再映射一次呢,我们通过下面一个图示来进行直观的查看吧,如下图6所示。第一次对所有需要映射的Segment进行映射,它是按照文件的编排,直接映射过去的,同时它也保证了能够分配到这样一段连续的虚拟地址。而第二次映射需要将对应的Segment映射到其头部表示的虚拟地址中,如图6中的C2,它需要被映射到最后,因此才需要进行两次装载,图中Virtual Address中的白色区域为mprotect部分。

图6 映射图示

接着继续分析代码

    /* Remember which part of the address space this object uses.  */

    l->l_map_start = c->mapstart + l->l_addr;

    l->l_map_end = l->l_map_start + maplength;

    l->l_contiguous = !has_holes;

 

    while (c < &loadcmds[nloadcmds])

      {

         if (c->mapend > c->mapstart

             /* Map the segment contents from the file.  */

             && (__mmap ((void *) (l->l_addr + c->mapstart),

                            c->mapend - c->mapstart, c->prot,

                            MAP_FIXED|MAP_COPY|MAP_FILE,

                            fd, c->mapoff)

                   == MAP_FAILED))

           goto map_error;

 

      postmap:

         if (c->prot & PROT_EXEC)

           l->l_text_end = l->l_addr + c->mapend;

         if (l->l_phdr == 0 && (ElfW(Off)) c->mapoff <= header->e_phoff &&

         ((size_t) (c->mapend - c->mapstart + c->mapoff)      >= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))

           /* Found the program header in this segment.  */

           l->l_phdr = (void *) (c->mapstart + header->e_phoff - c->mapoff);

 

         if (c->allocend > c->dataend)

           {

             /* Extra zero pages should appear at the end of this segment,

                after the data mapped from the file. 

                在从文件映射的数据之后,此段的末尾应出现额外的零页。*/

             ElfW(Addr) zero, zeroend, zeropage;

 

             zero = l->l_addr + c->dataend;

             zeroend = l->l_addr + c->allocend;

             zeropage = ((zero + GLRO(dl_pagesize) - 1)

                            & ~(GLRO(dl_pagesize) - 1));

 

             if (zeroend < zeropage)

               /* All the extra data is in the last page of the segment.

                    We can just zero it.  */

               zeropage = zeroend;

 

             if (zeropage > zero)

               {

                   /* Zero the final part of the last page of the segment.  */

                   if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0))

                     {

                       /* Dag nab it.  */

                       if (__mprotect ((caddr_t) (zero

                                                      & ~(GLRO(dl_pagesize) - 1)),

                                         GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)

                         {

                            errstring = N_("cannot change memory protections");

                            goto call_lose_errno;

                         }

                     }

                   memset ((void *) zero, '\0', zeropage - zero);

                   if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0))

                     __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),

                                  GLRO(dl_pagesize), c->prot);

               }

             if (zeroend > zeropage)

               {

                   /* Map the remaining zero pages in from the zero fill FD.

                   从零填充FD映射剩余的零页面。*/

                   caddr_t mapat;

                   mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,

                                     c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,

                                     -1, 0);

                   if (__builtin_expect (mapat == MAP_FAILED, 0))

                     {

                       errstring = N_("cannot map zero-fill pages");

                       goto call_lose_errno;

                     }

               }

           }

         ++c;

      }

继续像上面一样根据实验libcac.so一步一步计算相关内容,红色部分内容计算如下:

    l->l_map_start =l->l_map_start;(最开始__mmap返回的地址值)

    l->l_map_end = l->l_map_start + 0x201020;

l->l_contiguous = 0;

再到绿色部分代码:

判断当前C是否为最后一个如果,当前C为C1,C1->mapend = 0x1000肯定是大于C1->mapstart = 0。因此在这个if判断后会进行__mmap

__mmap ((void *) (l->l_addr + c->mapstart),

                     c->mapend - c->mapstart, c->prot,

                     MAP_FIXED|MAP_COPY|MAP_FILE,

                     fd, c->mapoff)

=》当前为C1:

=》__mmap(l->l_map_start,0x1000,C1->prot,MAP_FIXED|MAP_COPY|MAP_FILE,fd,0),

上述情况是对C1进行了映射,映射地址为第1小节中返回的那个l->l_map_start,同时采用MAP_FIXED的方式进行。

继续往下看存在

       if (l->l_phdr == 0 && (ElfW(Off)) c->mapoff <= header->e_phoff &&

              ((size_t) (c->mapend - c->mapstart + c->mapoff) >= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))

         /* Found the program header in this segment.  */

         l->l_phdr = (void *) (c->mapstart + header->e_phoff - c->mapoff);

上述代码与映射没什么关系先不管

继续往下看:

if (c->allocend > c->dataend)

我们先来看C1,C1->allocend = 0x774,C1->dataend = 0x774所以C1的映射已经结束,然后继续while,来进行C2所对应的Segment的映射。

__mmap ((void *) (l->l_addr + c->mapstart),

                     c->mapend - c->mapstart, c->prot,

                     MAP_FIXED|MAP_COPY|MAP_FILE,

                     fd, c->mapoff)

=》当前为C2

=》__mmap(l->l_map_start+0x200000,0x2000,C2->prot,MAP_FIXED|MAP_COPY|MAP_FILE,fd,0);

这里是C2对应的Segment的映射,继续往下

if (c->allocend > c->dataend)

这里C2->allocend = 0x201020,C2->dataend = 0x201010,所以在C2对应的段在映射时会进入到这个if中,我们进去到这个if里看里面是什么内容。

它定义了如下的三种数据,zero、zeroend、zeropage,冰球对变量进行了赋值。

           ElfW(Addr) zero, zeroend, zeropage;

           zero = l->l_addr + c->dataend;

           zeroend = l->l_addr + c->allocend;

           zeropage = ((zero + GLRO(dl_pagesize) - 1)

                     & ~(GLRO(dl_pagesize) - 1));

跟着这个赋值来计算相应的值,对于C2:

zero = l->l_map_start + 0x201010

zeroend = l->l_map_start + 0x201020

zeropage = (l->l_map_start+0x201010+0xfff)&(0x000)

为了继续计算,不妨我们给l->l_map_start一个初始值,mmap的返回值都是4096的整数倍,因此这个值可以很好地设置,例如设l->l_map_start为0x6a6c8000

因此:

zero = 0x6a6c8000+0x201010 = 0x6a8c9010

zeroend = 0x6a6c8000 + 0x201020 = 0x6a8c9020

zeropage = (0x6a6c8000+0x201010+0xfff)&(0x000) = 0x6a68ca000

 

if (zeroend < zeropage)

zeropage = zeroend;

if (zeropage > zero)

{

       /* Zero the final part of the last page of the segment.  */

       if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0))

       {

              /* Dag nab it.  */

              if (__mprotect ((caddr_t) (zero &  ~(GLRO(dl_pagesize) - 1)),

                            GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)

              __mprotect(0x6a8c9000,4096,C2->prot|PROT_WRITE)

              {

                     errstring = N_("cannot change memory protections");

                     goto call_lose_errno;

              }

       }

       memset ((void *) zero, '\0', zeropage - zero);

       if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0))

                __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),

                           GLRO(dl_pagesize), c->prot);

       }

 

       if (zeroend > zeropage)

       {

              /* Map the remaining zero pages in from the zero fill FD.

              从零填充FD映射剩余的零页面。*/

              caddr_t mapat;

              mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,

                     c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,

                     -1, 0);

        }

Linux动态链接与动态库加载全解析:从启动到符号绑定 动态链接是现代Linux程序的基础机制,它将链接操作从编译期延迟到装载期,让多个进程共享同一份库文件的物理内存,同时也让依赖升级无需重新编译。理解动态库加载流程,需要掌握内核如何通过PT_INTERP启动动态链接器、NEEDED依赖如何解析、GOT与PLT如何实现延迟绑定,以及dlopen系列接口在插件架构中的正确用法。实际工程中,符号冲突、版本不匹配、LD_LIBRARY_PATH优先级等问题,往往需要借助readelf、ldd、LD_DEBUG等工具逐层定位。本文从动态链接原理出发,结合GOT/PLT、 阅读详情

相关推荐

glibc 架构与模块实现:Linux 系统背后的动态链接与内存管理

在 Linux 系统运行中,glibc 作为用户态与内核之间的核心桥梁,隐藏在所有进程的底层。它以分层架构与模块化组织方式,将平台无关逻辑与平台相关实现分离,使得 x86_64、ARM64、RISC-V 等不同架构能够复用大部分代码。动态链接器 ld.so 负责装载与重定位,malloc 通过 arena 与 mmap 管理内存,系统调用封装层则将高级函数调用翻译为内核指令。理解这些机制对排查进程启动慢、内存占用高、容器迁移兼容性等问题至关重要。从基础概念出发,掌握 glibc 的工作原理,便能深入理解 L

weixin_30225755的博客 347

Linux动态库加载函数dlopen源码梳理(二)

在https://blog.youkuaiyun.com/SweeNeil/article/details/83744843 中大概梳理了整个流程,还有_dl_map_object_from_fd(),以及link_map结构没有进行分析,在这里对这两部分进行分析 由于_dl_map_object_from_fd()比较长,整个函数的代码就放到最后作为附录,前面部分来一点点进行梳理。 一、_dl_map...

SweeNeil 6729

Linux 二进制分析-ELF二进制格式(chapter 2.1&2.2)

ELF二进制分析,二进制格式

犇叔的博客 1214

win7 mount到Linux下无法分配内存的问题(Cannot allocate memory)

方法一 当通过mount.cifs命令对windows下的文件进行映射时,若文件太大,便会产生这种错误,解决方法是: 修改注册表HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanServer\Parameters\IRPStackSize项的值大于等于15,若IRPStackSize项不存在,就新建一个DWORD值...

佛道教主 2543

SVG文件上传漏洞实战:从XSS攻击到防御策略

本文深入剖析了SVG文件上传漏洞的成因与危害,揭示了攻击者如何利用SVG的XML本质构造恶意文件,绕过常规检查实现存储型XSS攻击。文章通过实战复现演示了完整的攻击链,并提供了从严格内容检查、安全解析过滤到设置CSP策略等多层次的纵深防御方案,帮助开发者从根本上加固文件上传功能。

weixin_29230805的博客 395

PCB布线避坑指南:为什么你的差分线等长总调不好?AD19蛇形等长技巧详解

本文详细解析了AD19环境下PCB差分线等长调整的核心技巧与常见问题。从差分信号等长的底层逻辑到AD19规则配置,再到蛇形等长高级技巧和信号完整性验证,帮助工程师解决差分线等长调整中的难题,提升高速PCB设计的信号质量。

weixin_28738397的博客 382

CentOS 安装 PHP

Linux安装配置phpWin7下安装Apache+PHP+MySQL在64位Windows 7系统上部署Apache + PHP + MySQL

阳光岛主 1万+

用MATLAB从零实现六足机器人步态仿真:交替三角与波动步态保姆级教程

本文详细介绍了如何使用MATLAB从零实现六足机器人的两种经典步态仿真:交替三角支撑步态和波动步态。通过参数定义、相位设置、运动轨迹生成和三维可视化等步骤,帮助读者掌握六足机器人步态规划的核心技术。教程包含完整代码和详细解析,适合机器人爱好者和相关专业学生学习实践。

weixin_26765363的博客 227

某些error page不加载_细说So动态库的加载流程

本文为看雪论坛优秀文章看雪论坛作者ID:sossaidlopen之内存装载dlopen用来打开一个动态链接库,并将其装入内存。它的定义在Android源码中的路径为/bionic/linker/dlfcn.cpp,执行流程如下:其核心代码在do_dlopen中实现,根据传入的路径或文件名去查找一个动态库,并执行该动态链接库的初始化代码。void* dlopen(constchar* f...

weixin_40007668的博客 650

[ESP32]:基于esp-modbus实现serial主机

[ESP32]:基于esp-modbus实现serial主机

LINKKEEE的博客 2567

fast-lio运行步骤(使用velodyne数据)

使用fast-lio运行velodyne数据

ericdowney 4769

Linux静态库与动态库:从原理到实践的完整对比

在Linux应用开发中,程序构建与部署绕不开库的链接问题。静态库(.a)与动态库(.so)是两种不同的代码复用方式:静态库在编译期将目标代码复制进可执行文件,动态库则通过运行时加载与符号绑定实现引用。理解两者的核心差异,是排查编译错误、运行时异常以及优化交付流程的关键。本文从编译原理、产物结构和加载机制入手,分析静态链接与动态链接的行为区别,详解符号解析、地址重定位等基础概念,并结合常见事故场景说明选型策略。通过实验对比,帮助你快速定位undefined reference、cannot open shar

weixin_28672681的博客 30

【量化入门】Tushare库获取公募基金的历史净值

在本文中,我们将介绍如何使用Python的Tushare库获取公募基金的历史净值。

木头大左的博客 1143

动态链接底层探秘:从GOT表、PLT到程序入口的完整链路

程序启动时,操作系统从ELF文件指定的入口开始执行,而这个入口并不是程序员熟悉的main函数。在main之前,还有一段隐藏的初始化链路,负责准备运行环境。与此同时,动态库与可执行程序的关联也依赖一组精细的机制:编译期写下的符号依赖,装载期由动态链接器解析并映射到虚拟内存。为避免地址冲突并支持多进程共享,系统引入了PIC(位置无关代码),通过GOT表和PLT实现延迟绑定与重定位。理解这些底层原理后,面对“找不到符号”“启动崩溃”“动态库不兼容”等问题,便能从符号解析和内存映射的角度精准定位。本文用实例带你观察

weixin_34344403的博客 287

Plant Simulation与ABB机器人虚实结合

Plant Simulation与ABB机器人虚实结合

santayana的专栏 1869

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

linux 装载动态库 c,linux/c下对动态库的编译和装载的学习

linux/c下对动态库的编译和装载的学习测试的方法是将一个日志函数(mr_errlog), 单独记录在一个C文件(errlog.c)中, 对该C文件编译为一个动态库(libtest.so), 再用测试程序(main.c)调用该动态库.1. 对动态库的编译对动态库的编译命令可以为:gcc -fPIC -shared -o libtest.so errlog.c其中gcc的选项说明如下:-fpic...

weixin_39854369的博客 242

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

ELF装载与进程虚拟内存:从文件到运行进程的完整链路

虚拟内存是现代操作系统进程管理的基石,它让磁盘上的可执行文件在运行时呈现为立体而复杂的进程地址空间。ELF文件通过程序头表将代码段、数据段按页映射到虚拟地址,内核建立VMA区域后并不会立即读取全部数据,而是等待CPU访问触发页错误,再按需调页从磁盘载入物理内存。动态链接机制借助GOT与PLT延迟绑定解析符号,实现代码共享与内存节省。理解这些原理,能有效解释进程VIRT内存异常、启动性能优化及内存占用等工程问题。本文结合实验梳理进程虚拟地址空间构建、缺页处理、动态链接装载与初始栈堆布置,还原从文件到运行进程的

weixin_33842304的博客 349

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【Linux】ELF、链接与动态库,程序装载全解析

这份内容系统梳理了 Linux 程序从源代码到可执行文件,再到进程运行的完整流程。重点介绍 ELF 文件结构、节与段、静态链接中的符号解析与地址重定位,以及动态链接中的动态库加载、PIC、GOT、PLT 和延迟绑定。同时结合虚拟地址空间,分析动态库从磁盘映射到内存及函数调用的全过程,并总结 `readelf`、`objdump`、`ldd` 等常用工具。

is_ko_nois_ko的博客 341

Linux静态库与动态库:从原理到实践讲透链接装载机制

在Linux系统编程中,库是代码复用与工程组织的基础,但很多开发者对静态库与动态库的底层机制一知半解。从链接器视角看,库的本质是符号集合:静态库通过ar打包目标文件,在编译期将代码合并进可执行文件;动态库则采用位置无关代码(-fPIC)配合SONAME机制,由动态链接器ld.so在运行时完成装载与重定位。理解这些原理,能帮助解决undefined reference、找不到共享库、符号冲突等经典问题。无论是初学者还是被动态库版本问题折磨过的工程人员,通过gcc、ar、readelf、objdump等工具实践

weixin_30899023的博客 198

Linux系统学习【全面剖析动静态库制作与使用、ELF与库加载的相关内容】

本文围绕 Linux 系统中的 Ext 系列文件系统展开深入学习,系统介绍 Ext2、Ext3 和 Ext4 的发展历程、设计思想以及核心技术特点。文章将从文件系统整体架构入手,详细解析超级块、块组、inode、数据块、目录项等关键数据结构,帮助读者理解 Linux 如何组织和管理磁盘中的文件。同时结合实际应用场景,介绍日志机制、文件读写流程、文件系统挂载、容量查看、检测与修复等常用操作。通过对 Ext 系列文件系统的原理分析与实践讲解,使读者能够掌握 Linux 文件存储管理的底层逻辑。

承于所学,探技术之渊;以代码求政,寻成长之道。 1万+

【Linux】库制作与链接原理:从静态库、动态库到 ELF、重定位、GOT 与 PLT

main.c↓ 预处理main.i↓ 编译main.s↓ 汇编main.o(ELF 可重定位目标文件)↓ 链接:目标文件 + 库 + 启动文件app(ELF 可执行文件或 PIE)↓ execve + ELF loader + 动态链接器(动态程序)进程地址空间编译负责把每个翻译单元变成.o;链接负责组合 Section、解析符号并应用重定位;装载负责根据 Program Header 建立进程映射;动态链接负责装载依赖库、解析动态符号并处理动态重定位。

星光编译者的技术博客,专注 C/C++、Linux、数据结构与算法。 406

内存分布和内存管理

堆内存管理机制 glibc是Linux系统的c运行库,最底层的api,几乎其他任何运行库都会依赖于glibc。一般是预先编译好,以二进制代码形式存在linux系统中。 不同平台的堆内存管理机制不同,glibc使用的是ptmalloc2的实现原理。 即glibc中的内存管理函数调用使用的是ptmalloc2的实现原理。 平台 堆内存分配机制 glibc ptmalloc2 Google tcmalloc Solaris(UNIX衍生) libumem free BSD(类

qq_36281031的博客 443

Segmentation fault 学习

在Linux下写代码,第一次遇到 Segmentation fault 简直是懵逼,看了大牛的,学习了一下。 http://blog.chinaunix.net/uid-23069658-id-3959636.html    (备注:我的glibc版本是2.12,GCC版本4.4.6,内核版本2.6.32-279)    Linux上开发时最恼火的就是遇到“Segmeta

lzj_lzj2014的博客 930

从.o到ELF-静态库动态库与可执行文件的生命周期

本文系统解析了从 .o 到 ELF 的二进制文件生命周期,聚焦库的本质与加载机制。静态库在链接期将代码“焊入”可执行文件,体积大但独立运行;动态库则通过 -fPIC 生成位置无关代码,运行时由 ld.so 动态加载,共享内存、节省空间。核心在于理解 ELF 格式:节(Section)是磁盘上的细粒度组织,段(Segment)是内存中的大块布局,二者通过 Program Header Table 实现虚拟地址空间的蓝图构建。该表由编译器生成,指导进程创建时的内存映射,揭示了虚拟地址空间的来源——编译器与操作系

xiongditian的博客 431

Linux静态库与动态库:从链接原理到工程实践详解

链接器是Linux下C/C++程序构建的关键环节,它决定了代码如何被组织与复用。静态链接把目标文件直接打包进可执行文件,生成体积大但易于部署的二进制;动态链接则依赖运行时加载器,通过位置无关代码(-fPIC)与soname机制实现多进程共享与灵活升级。在实际开发中,编译期报错与运行期加载失败往往根源不同,掌握gcc的-L/-l/-rpath参数、ldd工具及动态链接器搜索规则,是定位问题的核心能力。从工具链命令到Makefile管理,再到dlopen插件的动态加载,合理选择静态库与动态库,能显著提升Linu

weixin_32715349的博客 307
上一篇: Linux页高速缓存回写机制机制源码分析,基于内核3.8:Writeback
下一篇: 操作系统免驱设备实现调研
SweeNeil
博客等级 码龄9年 251粉丝 86原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值