Valgrind Memcheck 源码分析

valgrind基本功能介绍、基础使用方法说明 拷贝效果不好,请看原文。1、Valgrind概述Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。 阅读详情

Valgrind Memcheck

日期:2017-12-27 18:10:53 星期三

一、源代码的基本情况

Valgrind版本

3.13.0

主要涉及文件

  • /memcheck/mc_main.c
  • /memcheck/mc_translate.c
  • /memcheck/mc_errors.c
  • /VEX/pub/libvex_ir.h

功能概述

Valgrind是用于构建程序动态分析工具的重量级插桩框架。目前Valgrind内置的工具,可用于检测内存管理和线程竞争等等过程中产生的问题,更快速、更准确和更详细地分析所需的程序。

其中,Memcheck是valgrind应用最广泛的工具。它是一个重量级的内存检查器,能够发现C或者C++在开发过程中绝大多数导致程序崩溃或者不可预知的行为的内存相关的错误,比如:使用未初始化的内存、使用已释放内存、内存访问越界等。


二、常见内存错误

Memcheck检查的是C和C++程序中通用的的一些内存错误。其中,文件/memcheck/mc_errors.c定义了这些内存错误的枚举类型,每个枚举项的解释见右边注释。

/* What kind of error it is. */
typedef 
   enum { 
      Err_Value, // 使用未初始化变量
      Err_Cond, // 使用未初始化值进行条件跳转
      Err_CoreMem, // 地址中包含未初始化字节
      Err_Addr, // 地址不可读或不可写
      Err_Jump, // 程序运行跳转至非法内存
      Err_RegParam, // Syscall寄存器参数包含未初始化字节
      Err_MemParam, // Syscall堆栈参数包含未初始化或不可寻址字节
      Err_User,
      Err_Free, // 释放了非法内存
      Err_FreeMismatch, // new/new[]/malloc的内存没用对应delete/delete[]/free释放
      Err_Overlap, // strcpy/memcpy等函数的src和dest地址有重叠
      Err_Leak, // 内存泄露
      Err_IllegalMempool, // 内存池地址非法
      Err_FishyValue, // 函数参数中带有非法值,如size_t类型传入了负数
   }
   MC_ErrorTag;

总结起来,有以下几种常见的内存错误:

使用未初始化内存

位于程序不同段的变量,其初始值是不同的。位于BSS段的全局变量和静态变量初始为0,而位于Stack的局部变量和位于Heap的动态分配的变量,其初始值为随机值。如果程序使用了为随机值的未初始化变量或者由未初始化变量派生出的变量,那程序行为无法预估。

内存读写越界

读写了不应该或者无读写权限访问的地址空间。比如数组读写越界、堆内存读写越界、经典的off-by-one问题。

内存覆盖

在使用标准库中提供的内存操作函数,如strcpy、memcpy、strcat等,src 和 dst 指向的地址存在重叠,导致最终操作不可预期。

动态内存管理错误

  1. 申请和释放不匹配
    用malloc/alloc/realloc申请的内存需要用 free 释放;用new/new[]申请的内存需要用delete/delete[]释放。不匹配的申请和释放操作会带不可预期的问题。

  2. 重复释放(Double Free)
    同一块动态分配的内存先后被释放多次,导致内存错误,也是一些安全问题的源头。

  3. 释放后使用(Use After Free)
    动态分配的内存释放后,继续进行操作。这类操作可能不会导致程序崩溃,但会导致数据错乱,黑客也可以此篡改malloc等分配器的结构体用于泄露或者修改内存。

内存泄漏

在程序中动态申请的内存,在使用完后即时没有释放,丢失相关引用,也无法被程序的其他部分访问,直到程序结束前仍然占用着。不断地内存泄露,会导致最终程序使用内存过大,产生OOM。


三、Memcheck工作原理

Valgrind概述

Valgrind是用于构建程序动态分析工具的重量级插桩框架,其中包含了很多出色的Valgrind工具,Memcheck是其中之一。

Valgrind的工具是在Valgrind内核基础上使用C语言编写的插件。最直观的表达就是:Valgrind内核 + 工具插件 = Valgrind工具。在Valgrind框架下编写一个二进制程序动态分析工具比从零开始简很多,因为Valgrind内核为新工具的编写提供了许多通用的工具集,比如错误记录、动态插桩等。

当Valgrind工具程序启动时,会将需要分析的程序加载与工具程序同一个进程空间中,然后使用JIT(just-in-time)的动态二进制重编译技术,将代码分成一个个小的代码块实施重编译。在重编译过程中,Valgrind内核会将相应代码块的机器码转化成中间表示,插件会在中间代码中进行相应分析代码的插桩,最后通过内核把中间表示转换成原本的机器码,在目标机器上执行。Valgrind内核大部分时间花在上述机器码和中间表示的相互翻译执行中,而原程序的所有机器码并没有执行,执行的都是插桩后的代码。

所有的Valgrind工具都是使用静态链接的可执行文件,里面包含了Valgrind内核和工具插件。虽然这样会导致每个工具程序中都需要包含一份Valgrind内核,内核大概2.5MB左右,稍微浪费一些磁盘空间,但是静态链接可以使整个可执行文件加载到非标准的启动地址,方便把待分析程序加载进同一个进程空间中,然后使用Valgrind重编译技术将待分析程序机器码重编译到别的地址执行。

Valgrind工作原理

工具启动

Valgrind工具,是在命令行中通过valgrind程序通过指定tool命令行参数为具体的工具名称来启动的。valgrind命令行程序只是一个很简单的封装程序,根据tool命令行来调用execv去执行目标Valgrind工具程序。然后Valgrind内核会先初始化各种子系统,如地址空间管理器、内部内存分配器等,再将待分析程序的.text和.data段等映射到同一个程序的地址空间中,并为其配置好堆栈等。接着,开始内核和插件中的命令行参数初始化和加载,并完成内核中更多子系统的加载,如翻译表、信号处理机制、线程调度器和调试信息等等。此时,Valgrind工具准备完毕,开始从待分析程序的第一条指令执行重编译和运行。

Valgrind内核和插件本身是运行在物理CPU上的,而待分析程序是运行在虚拟出的CPU上。由于有JIT手段来加速,整体看起来不像在解析执行,执行效率也比较高。物理CPU是直接使用真实CPU上的寄存器的,而虚拟CPU使用的寄存器则是保存在内存中的虚拟寄存器,同时影子寄存器也是类似的方法方便模拟出来。Valgrind为每个线程提供一个ThreadState结构,用于保存所有的虚拟寄存器和影子寄存器,方便后续的分析工作。

中间表示(VEX)

在Valgrind的重编译的过程中,使用的中间表示是一种平台无关的语言——VEX,通过屏蔽硬件平台的差异性,节省了大量针对不同平台的插桩代码。

Statement(结构体IRStmt)表示有副作用的操作,如写寄存器、写内存、临时变量赋值等。其中,Statement由Expression组成。Expression(结构体IRExpr)表示没有副作用的操作,如读内存、做算术运算等,这些操作可以包含子表达式和表达式树。

在Valgrind中,代码被分解成多个小的代码块,每个代码块里包含VEX的Statement列表。每个代码块的结构体是IRSB,IRSB是单入口多出口的,代码如下所示:

typedef
   struct {
      IRTypeEnv* tyenv; // 表明IRSB中每个临时变量的类型
      IRStmt**   stmts; // VEX语句列表
      Int        stmts_size; // Statements总长度
      Int        stmts_used; // 实际上使用的Statements的数目
      IRExpr*    next; // 下一跳的位置
      IRJumpKind jumpkind; // 最后代码块结束jump的类型
      Int        offsIP; // IP寄存器的偏移
   }
   IRSB;

Valgrind根据一定规则将代码划分为很多小代码块后,会进行以下八个阶段,将插件的分析代码进行插桩并优化:
1. 反汇编:机器码 → 树状中间表示
2. 优化1:树状中间表示 → 扁平中间表示
3. 插桩: 扁平中间表示 → 带桩的扁平中间表示
4. 优化2:带桩的扁平中间表示 → 优化的扁平中间表示
5. 重建树:优化的扁平中间表示 → 带桩的树状中间表示
6. 汇编选择:带桩的树状中间表示 → 目标汇编代码
7. 寄存器分配:目标汇编代码 → 寄存器优化的目标汇编代码
8. 汇编:寄存器优化的目标汇编代码 → 机器码

JIT执行

通过上面的插桩优化后得到的机器码会保存到一个定长、使用线性探测的哈希表中。如果哈希表达到了80%的程度,使用FIFO的策略将八分之一旧的重编译得到的机器码丢弃。

当一个代码块执行完毕,下一个代码块的路由是靠dispatcher和scheduler两个组件来实现。首先,翻译后的代码执行完毕后,代码执行会陷入由汇编代码写的dispatcher,dispatcher内部有一个小的保存最近使用代码块的快速缓存,用于快速查找下一个需要用到的代码块,这样切换的效率非常高。

如果快查找失败了,控制流会到用C写的scheduler上面。scheduler会在上述的哈希表中查找已经翻译好的代码块,找不到的话,会进行重编译并加入哈希表中,还有更新dispatcher内部的快速缓存,以便dispatcher快速的后续使用。

对于system call、signal处理、事件机制等就不展开讨论了。

Valgrind回调Memcheck

static void mc_pre_clo_init( void );
static void mc_post_clo_init ( void );
IRSB* MC_(instrument) ( VgCallbackClosure* closure,
                        IRSB* sb_in,
                        const VexGuestLayout* layout,
                        const VexGuestExtents* vge,
                        const VexArchInfo* archinfo_host,
                        IRType gWordTy, IRType hWordTy );
static void mc_fini ( Int exitcode );

上述四个接口,由Valgrind在不同阶段调用。其中,mc_pre_clo_init 和 mc_post_clo_init用于初始化memcheck插件,MC_(instrument) 是在scheduler需要translate的插桩阶段会调用。

   VG_(track_new_mem_startup)     ( mc_new_mem_startup );
   VG_(track_new
手把手教你改造OSGEarth 3.1源码,实现本地瓦片数据加载(附源码下载) 本文详细介绍了如何通过改造OSGEarth 3.1源码实现本地瓦片数据加载,包括环境搭建、源码分析、核心改造步骤及性能优化技巧。通过自定义驱动模块和GDAL集成,开发者可以构建高效、稳定的离线三维地理信息系统,显著提升加载速度和系统可靠性。 阅读详情

相关推荐

Linux C/C++进阶:从系统原理到AI Infra、音视频等核心领域实战

在计算机系统开发领域,深入理解操作系统、网络和编译器原理是构建高性能、高可靠性软件的基础。这些底层知识构成了从基础服务到前沿应用的技术支柱,其核心价值在于实现对计算资源的精细控制和极致性能优化。掌握Linux系统编程、现代C++特性及并发模型,使开发者能够应对高并发、低延迟的复杂工程挑战。这种能力在AI基础设施(AI Infra)、音视频处理、嵌入式系统等垂直场景中尤为重要,例如在大模型推理优化、实时流媒体传输等具体实践中,扎实的系统级编程功底是技术突破的关键。本文聚焦于为有一定基础的C/C++开发者规划一

weixin_34220179的博客 437

2026年地平线嵌入式岗位高频面试题带参考答案

参考答案我深度参与过边缘AI视觉感知网关参考答案I2C(Inter-Integrated Circuit)是飞利浦推出的两线式串行总线,用于板内芯片间短距离通信,是嵌入式最常用的外设总线之一。参考答案字节序是指多字节数据在内存中的存储顺序,是嵌入式跨平台、跨设备通信必须关注的基础概念,分为大端、小端两种模式。大端模式(Big-Endian)数据的高位字节存放在低地址,低位字节存放在高地址,存储顺序符合人类读写数字的习惯。例:32位数据0x12345678。

Open 62

valgrind-3.13.0源码

压缩包为valgrind-2.13.0源码,需要的同学自行下载,也可以到官网下载,官网可能会有更新的版本。

valgrind原理图

valgrind结构图,描述了Valgrind的基本结构,有助于了解Valgrind的基本原理

[Unity]-[UI]-[Image] 关于UI精灵图资源导入设置的详细解释

如果纹理存储了有特定含义的信息,并且您需要着色器中的确切值(例如,平滑度或金属度),请禁用此属性。此选项允许将纹理编码为特定格式(RGBM 或 dLDR,具体取决于平台)并通过后期处理步骤对纹理数据进行处理(推拉式扩张通道)。使用在3D模型的专业法线贴图。通用的纹理类型,一般在Material上的材质图片参数使用,比如漫反射、法线贴图这些。​​Compression​​:ASTC 6x6(移动端)或DXT5(PC)​​Compression​​:BC5(PC)或ASTC 6x6(移动端)

sky_smile_Allen的博客 2171

STM32 BLDC无刷电机霍尔信号捕获与换相控制实战解析

本文深入解析了STM32如何实现BLDC无刷电机的霍尔信号捕获与换相控制。详细阐述了硬件接口配置、定时器霍尔传感器模式的使用、抗干扰的滤波器与软件容错设计,以及中断服务与换相逻辑的实战代码。重点解决了信号抖动、换相不准等常见问题,旨在帮助开发者构建稳定可靠的电机驱动基础。

weixin_29193259的博客 368

RT-Thread 5.1.0 实战:手把手教你用CubeMX和RT-Studio创建第一个工程(附常见编译错误修复)

本文详细介绍了如何使用STM32CubeMX和RT-Thread Studio 5.1.0创建第一个RT-Thread工程,包括开发环境准备、CubeMX配置、工程整合及常见编译错误修复。通过实战指南,帮助开发者快速上手RT-Thread 5.1.0,解决开发过程中的典型问题,提升嵌入式开发效率。

weixin_29165465的博客 286

GD32官方资料学习体系(主要包括MCU选型、原理图PCB设计、软件开发入门及用户手册等)

大部分还未更新,先占个坑,日后更新…… 目前国外芯片涨价涨的离谱(就是在说ST),国内最好的替代就是兆易创新出的系列芯片了。但是官方资料繁杂,小白上手不易,因此边入门边记录,也给后来者根据官方资料可以有一个较为完整的学习体系。

Eddie的博客 1万+

TOGAF9.2/10 认证基础知识点概览表

(考点解析:业务场景是一种重要的技术,可以在企业架构的各个阶段使用,主要是架构愿景和业务架构,但如果需要,也可以在其他架构域中使用,以直接从业务的高级需求中派生架构的特征。“哪里”可以看作是对相关企业的界定、“为什么”可以被视为组织环境中的关键驱动因素和要素、“谁”是指确定发起人利益相关者和受业务指令影响的其他主要利益相关者,以创建企业架构,并确定他们来自企业的需求和优先级、他们与企业的关系以及彼此之间所需的工作行为。它是业务驱动和业务主导的,它结 合了所有业务线的必要努力,以实现所需的能力。

2302_81497763的博客 2077

Unity插件UniWebView实战:UGUI动态调整内嵌网页尺寸与交互优化

本文详细解析了如何在Unity游戏中使用UniWebView插件实现内嵌网页的动态尺寸适配与深度交互。核心在于利用Reference Rect Transform属性,将网页窗口精准绑定至UGUI控件,并随其动态调整。文章进一步探讨了通过消息系统与JavaScript执行实现Unity与网页的双向通信,并提供了加载优化、过渡动画及事件拦截等实用技巧,助力开发者打造无缝、流畅的游戏内网页体验。

earth的博客 276

从零到一:Qt与PCL的跨平台开发环境搭建全攻略

本文详细介绍了从零开始搭建Qt与PCL跨平台开发环境的完整流程,涵盖Windows、Linux和macOS三大平台。重点解析了Qt5与Qt6版本选择、PCL库安装配置、VTK集成等关键技术要点,并提供跨平台CMake项目模板和常见问题解决方案,助力开发者快速构建三维点云处理应用。

weixin_29280657的博客 384

Boot过程相关镜像详解:IVT、DCD、App Boot镜像到底是什么?

S32系列芯片中常说的IVT镜像、DCD镜像等跟Boot过程相关的镜像到底是什么,镜像结构长啥样?各镜像作用是什么?主分区和备份区的概念?答案统统在这里!!

车载系统攻城狮 4253

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: Linux随机数发生器
下一篇: 关于“自然语言”的一些思考
Linsoft1994
博客等级 码龄17年 17粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值