RISC-V code model(medlow/medany)原理与混用实战
排查过一个怪问题某 RISC-V 工程的中间件静态库和主目标用了不同的 code model我想确认库到底是 medlow 还是 medany。随手数了一下反汇编里 lui 和 auipc 的指令比例auipc 远多于 lui于是判断这是 medany。结论是错的差点据此改了配置。后来才搞清楚code model 的判别根本不看指令比例而是看重定位类型。下面把 RISC-V 的 code model 机制、medlow 和 medany 的差异以及主目标 medany 中间件库 medlow 混用这个真实案例讲清楚重点就是那个容易踩错的判别方法。一、code model 到底在管什么RISC-V 有个先天约束没有单条指令能直接装一个 32 位立即数。luiLoad Upper Immediate和auipcAdd Upper Immediate to PC都只能装 20 位立即数要凑一个完整 32 位地址得再配一条addi或 load/store补上低 12 位。20 位加 12 位刚好 32 位。问题来了这 20 位立即数到底代表什么如果是绝对地址的高 20 位那lui装的就是符号在内存里的真实位置的高位如果是相对 PC 的偏移的高 20 位那auipc装的就是符号离当前这条指令有多远的高位。同一个 20 位立即数两种解释方式生成出来的代码寻址行为完全不同。code model 就是来回答这个问题的。它告诉编译器两件事你可以假设整个程序的地址落在哪个范围以及用哪种寻址方式生成访问全局符号的代码。选了 medlow编译器就用绝对地址那一套选了 medany就用 PC 相对那一套。为什么需要这个东西因为编译器在生成访问全局变量的指令时得知道用lui还是auipc得知道算出来的地址能不能落在目标范围里。code model 就是编译器和链接器之间的一个约定约定好了链接器才知道怎么回填重定位。二、medlow写死的绝对地址medlowmedium-low的指令序列长这样lui a0, %hi(sym) # 装符号绝对地址的高 20 位 addi a0, a0, %lo(sym) # 补低 12 位lui把符号的绝对地址高 20 位装进寄存器addi再补上低 12 位拼出符号的真实绝对地址。这个地址是写死的跟代码运行在哪个位置没关系——只要符号的绝对地址落在 medlow 假设的范围内就行。这个范围是0x0到0x7FFFFFFF也就是低 2GB。为什么是 2GB这要从 RISC-V 立即数的符号扩展说起。lui装的 20 位立即数会左移 12 位放进寄存器高位低 12 位由addi补而addi的 12 位立即数是有符号的范围 -2048 到 2047。所以最终地址 (hi20 12) sign_extend(lo12)。当 hi20 取最大正值 0x7FFFF、lo12 取最大正值 0x7FF 时地址 0x7FFFF000 0x7FF 0x7FFFF7FF这是正数范围的上限。官方 psABI 文档写得很清楚medlow 在 RV32 上能寻址整个 4GB 空间里符号扩展后落在正数区间的部分精确范围是0x0到0x7FFFF7FF日常说低 2GB是个近似。medlow 的特点是不限制代码运行在哪个位置因为寻址用的是符号绝对地址跟 PC 无关但限制所有符号的绝对地址必须落在这个 2GB 范围内。一旦哪个符号地址超过 0x7FFFFFFFmedlow 就寻址不了链接器会报 relocation truncated to fit 之类的错。这里有个细节值得展开。官方 psABI 规定R_RISCV_HI20的计算公式是(symbol_address 0x800) 12低 12 位则是symbol_address本身。为什么高 20 位要先加 0x800 再右移因为低 12 位的有符号偏移范围是 -2048 到 2047。当一个符号地址的低 12 位落在 0x800 到 0xFFF 时把它当有符号数看就是负数-2048 到 -1这时候高 20 位得借一位过去否则 (hi2012) sign_extend(lo12) 就凑不回原地址。加 0x800 就是做这个四舍五入到最近页的进位保证最终拼出来的地址精确等于符号地址。这个细节平时不用管但理解了就知道 medlow 的高 20 位加低 12 位不是简单拼接而是带符号的拼合。三、medany跟着 PC 走medanymedium-any的指令序列长这样.Ltmp0: auipc a0, %pcrel_hi(sym) # 取当前 PC 为基准装偏移高 20 位 addi a0, a0, %pcrel_lo(.Ltmp0) # 补低 12 位auipc把当前 PC 值 偏移高 20 位算出来放进寄存器addi再补低 12 位最终地址 PC 偏移。这里的关键是auipc用的是当前 PC不是某个写死的绝对地址。medany 的假设范围不一样它不限制符号的绝对地址只要求每个符号离引用它的那条指令不超过 ±2GB。只要满足这个符号在内存里随便放medany 都能寻址到。为什么 medany 更适合位置无关和可重定位代码因为寻址基准是 PC。假设整个代码段从 0x61000000 搬到 0x62000000所有指令的 PC 一起变了符号地址也一起变了但符号离引用点的偏移没变。auipc 算出来的还是对的。medlow 就不行代码搬走了lui 里写死的绝对地址还是老的直接寻址错。这就是 medany 名字里any的含义不挑绝对地址位置只要相对距离够近。而 medlow 的low指的是地址必须在低 2GB。顺带提一个 medlow 在 RV64 上的细节。前面说的低 2GB 是 RV32 的情况。到了 RV64寄存器是 64 位lui装的 20 位立即数左移 12 位后还要做符号扩展所以 medlow 实际能寻址的是两个区间低 2GB0x0到0x7FFFF7FF和高 2GB符号扩展后的负数区0xFFFFFFFF7FFFF800到0xFFFFFFFFFFFFFFFF。中间那一大段够不着。所以 RV64 上 medlow 也不是只能用低 2GB但中间那段空洞是它的硬伤。本工程是 RV32地址都在低 2GBmedlow 用着没问题。还有一个和 medany 强相关的优化叫 linker relaxationRELAX。RISC-V 的auipc jalr调用序列如果链接器发现目标函数就在 ±2KB 范围内会把它压缩成一条jal省一条指令还省 4 字节。这个优化对 medany 和 medlow 都适用但 medany 因为是 PC 相对调用大多集中在附近relax 命中率高medlow 的绝对地址调用跨度大命中率低。所以同样一份代码medany 编译出来往往比 medlow 体积略小这也是现代工具链默认 medany 的一个现实理由。四、判别 code model别数指令看重定位类型这一节是我踩坑的地方也是全文最想说的。排查那个中间件库的 code model 时我数了反汇编里 lui 和 auipc 的条数发现 auipc 有 3253 条lui 只有 1873 条auipc 远多于 lui。按auipc 多就是 medany的直觉我判断库是 medany。错了。为什么数指令比例会错因为auipc在 medlow 代码里也会大量出现而且可能比 lui 还多。原因有两个第一函数调用。call sym在两种 code model 下都展开成auipc ra, %pcrel_hi(sym)jalr ra, ra, %pcrel_lo(sym)这是 PC 相对调用跟 code model 无关。不管 medlow 还是 medany函数调用都用 auipc。第二gp 相对小数据访问。.sdata/.sbss段的小数据由-msmall-data-limit控制访问它们用auipc相对__global_pointer$这也跟 code model 无关。所以一个 medlow 编译的库里auipc 完全可能比 lui 多得多。指令比例没有区分度。正确的判别方法是看重定位类型。code model 决定的本质是访问全局符号时用哪种重定位这才是权威依据。用objdump -r看重定位重定位类型含义对应 code modelR_RISCV_HI20R_RISCV_LO12_I/LO12_S绝对地址高 20 位 低 12 位medlowR_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_I/LO12_SPC 相对高 20 位 低 12 位medany只有HI20对PCREL_HI20这一对才是区分标志。R_RISCV_CALL函数调用PC 相对、R_RISCV_BRANCH/R_RISCV_JAL跳转PC 相对在两种 model 下都出现不能用来判别。还有一个坑未链接的.o/.a文件里lui的操作数显示为0x0因为绝对地址还没回填那是重定位占位符。所以判别未链接文件必须用objdump -r看重定位类型不能看反汇编的 lui 操作数。要看真实绝对地址得对已链接的 elf 反汇编。判别命令可以直接复制# 对未链接的库看重定位类型 LIBlibxxx.a echo HI20(绝对/medlow): $(riscv64-unknown-elf-objdump -r $LIB | grep -c R_RISCV_HI20) echo PCREL_HI20(PC相对/medany): $(riscv64-unknown-elf-objdump -r $LIB | grep -c R_RISCV_PCREL_HI20) # medlow: HI20 0, PCREL_HI20 0 # medany: HI20 0, PCREL_HI20 0五、地址布局为什么两种 model 都合法光知道机制还不够得看实际工程的地址布局能不能同时满足两种 model 的假设。某 RISC-V MCU SDK 的链接脚本化名ez_demo.lds定义的内存布局是sram : ORIGIN 0x60000000, LENGTH 218K # RAM放 data/bss/堆栈 rom : ORIGIN 0x61000000, LENGTH 1M # Flash放 text/rodataXIP 执行所有代码和数据地址都在0x60000000到0x610FFFFF之间远小于0x80000000。这个布局对两种 model 都成立对混用也成立。medlow 没问题所有符号绝对地址都小于 0x80000000落在它假设的低 2GB 内。medany 也没问题所有符号相对 PC 的偏移远小于 ±2GB毕竟整个地址空间才 1MB 出头。混用之所以能跑是因为静态链接时 medlow 库的绝对地址重定位和 medany 主目标的 PC 相对重定位由链接器分别独立计算回填互不干扰。链接器看到一条R_RISCV_HI20就按绝对地址算看到一条R_RISCV_PCREL_HI20就按 PC 相对算各算各的最后都填进同一个 elf 里不冲突。这就是主目标 medany 中间件库 medlow能正常链接运行的原因地址布局同时满足两种 model 的假设混用没有功能问题。六、实测案例主目标 medany 中间件库 medlow某 RISC-V MCU SDK 的 code model 设置是这样的主目标所有应用工程用 medany设置在tools/cmake/toolchain.cmake化名里编译标志-marchrv32imac -mabiilp32 -mcmodelmedany中间件静态库用 medlow设置在ez_middleware/CMakeLists.txt化名里编译标志-mcmodelmedlow -Os。release 模式不重新编译中间件而是直接链接一个预编译库libez_ble_m4s1.a化名这个预编译库就是 debug 方式从源码编译出来的产物。用重定位类型一查结论很直接目标code modelR_RISCV_HI20R_RISCV_PCREL_HI20主目标126 个 .obj 聚合medany04329中间件库debug 编译medlow17420预编译库release 用medlow17420主目标 HI20 是 0、PCREL_HI20 有 4329 条是 medany两个中间件库 HI20 都是 1742、PCREL_HI20 都是 0是 medlow。预编译库和 debug 编译库的重定位计数完全一致说明 release 用的预编译库就是 debug 编译产物同一个 medlow 库没有第三套配置。光看重定位计数还不够我又对比了两个库的字节级特征。预编译库libez_ble_m4s1.a化名的大小是 414664 字节debug 编译产物一字不差也是 414664 字节。md5sum对一下哈希一致。计数相同可能是巧合文件大小和哈希一致基本就是同一个文件了。所以可以放心下结论release 链接的就是 debug 编译出来的那个库没有第三套编译配置。排查时不用纠结预编译库会不会是另一个 code model它就是 medlow。反汇编对比更直观。medany 主目标里寻址全局变量g_bleList化名610018f2: auipc a0, 0x0 # 取当前 PC 为基准 610018f6: addi a0, a0, 510 # PC 510 0x61001af0 g_bleList地址 PC 偏移跟代码加载位置无关。medlow 库里寻址字符串__func__.761026a44: lui a2, 0x6102a # 绝对地址高 20 位 61026a4c: addi a2, a2, -2012 # 0x6102a000 (-2012) 0x61029824地址是写死的绝对值跟代码位置无关但要求地址小于 0x80000000。两个函数形态相似都是装高 20 位 加低 12 位两条指令但lui绝对和auipcPC 相对的区别一目了然。七、混用要不要统一既然混用功能无碍那要不要统一成一种 code model我的建议是保持现状别动。最直接的理由是当前 elf 就是 medany 主目标加 medlow 库链接出来的产物build success运行正常。一个能跑的配置没有功能收益就没有改的动力。真要统一往哪个方向改都麻烦。统一到 medany中间件库得改CMakeLists.txt一行但还得重新编译并提交预编译库libez_ble_m4s1.a这是 release 交付物动它就是动 release 流程。统一到 medlow 更糟改toolchain.cmake会让全 SDK 所有项目所有源文件重编风险面铺得很大。两个方向都是只增加成本不增加收益。从工程合理性看现状本身也说得通。主目标代码量大、分散在 flash 各处medany 的 PC 相对寻址不依赖绝对地址布局将来 flash 基地址变动更稳健库用 medlow 也无妨因为库地址也在 0x7FFFFFFF 内。两者各得其所没必要为了统一这个形式把它们拉齐。唯一的隐患是移植。哪天这个工程要移植到地址超过 2GB 的平台medlow 库就会失效它的 lui 装不下超过 0x7FFFFFFF 的绝对地址。但只要地址布局还在低 2GB这个隐患就不会触发。如果团队有全工程单一 code model的硬性规范必须统一那推荐库改 medany只改一行加重新生成预编译库主目标不动影响面最小而且 medany 是 GCC 较新工具链的默认值更现代。八、写在最后回过头看那个踩坑根子在于把指令形态当成了寻址语义。medlow 和 medany 生成的都是 lui/auipc 加 addi 的两条指令序列形态太像了但重定位类型和地址含义完全不同。数指令比例是在看形态看重定位类型才是在看语义。判别 code model记住一句话看重定位类型别数指令。你的项目 code model 统一吗混用过 medlow 和 medany 吗有没有踩过类似的看错判别依据的坑评论区聊聊有用的话点个在看让更多搞 RISC-V 的同行看到。RISC-V #code模型 #medlow #medany #嵌入式开发

相关新闻

C# WinForm TCP-Socket

C# WinForm TCP-Socket

一、TCP 基础理论知识点 对应代码1.1 TCP 四大核心特点(背诵知识点)面向连接:通信前三次握手建立连接流式无边界传输:数据连续流传输,需要固定缓冲区接收可靠传输:自带确认重传,无丢包全双工&a…

2026/8/26 19:10:54 阅读更多 →
Istio 服务网格入门:流量管理、安全与可观测性

Istio 服务网格入门:流量管理、安全与可观测性

Istio 服务网格入门:流量管理、安全与可观测性工具地址:https://www.speedce.com 社区论坛:https://bbs.speedce.com 联系:speedceadsgmail.com写在前面 服务网格增加了复杂度,外部可达性验收更重要。 本文是一份围绕「…

2026/8/26 19:10:54 阅读更多 →
16|设计一个AI业务知识分析台:它首先应该解决BA的哪些任务?

16|设计一个AI业务知识分析台:它首先应该解决BA的哪些任务?

食味里“川香鸡腿饭套餐”项目结束后,BA林悦做了一次复盘。她发现自己每天都在不同工具之间搬运同一批业务信息。 企微里是商品、研发、供应链和门店的讨论;录音转写里藏着尚未确认的业务规则;OA附件里有新品审批;Excel里维护产品…

2026/8/27 20:07:29 阅读更多 →

最新新闻

Agent任务拆解:从Prompt到可校验可追踪的稳定工程流程

Agent任务拆解:从Prompt到可校验可追踪的稳定工程流程

很多开发者在搭完第一个 Agent 的时候,都会经历一个类似的心路历程:Demo 阶段一切都很丝滑,让大模型查资料、写代码、调工具,每一步都像模像样;一旦把真实业务接进系统,Agent 就开始花式翻车。要么漏掉关键…

2026/8/27 21:12:25 阅读更多 →
LTC2946电源轨监测实战:I2C接口下的电压电流功率与能量测量

LTC2946电源轨监测实战:I2C接口下的电压电流功率与能量测量

做电源轨监测这几年,我越来越觉得“能测电压”和“会算电能量”是两回事。去年在改一台48V工业设备的电源板时,主控要同时盯住电压、电流、瞬时功率,还要统计整块板子上电过程的电荷和能量,接口最好用I2C,方便直接挂到…

2026/8/27 21:12:25 阅读更多 →
Freescale高灵敏度加速度计应用实践:选型、硬件与调试全攻略

Freescale高灵敏度加速度计应用实践:选型、硬件与调试全攻略

Freescale的高灵敏度加速度计,说实话在MEMS传感器圈子里算是一代经典。我最早接触这个系列是在做工业状态监测的项目,当时需要检测低速重载设备的微小振动,找了一圈发现Freescale(现在叫NXP)的MMA系列在分辨率和噪声特…

2026/8/27 21:12:25 阅读更多 →
AD/DA转换器原理、选型与PCB设计避坑指南

AD/DA转换器原理、选型与PCB设计避坑指南

1. 项目缘起:从“未知引脚”到信号世界的桥梁最近在调试一个嵌入式项目时,遇到了一个让人头大的问题:系统读取到的传感器数据总是飘忽不定,时准时不准。用万用表量模拟信号是稳定的,但到了微控制器里就变成了“跳跳糖”…

2026/8/27 21:12:25 阅读更多 →
C语言strcmp模拟实现:底层内存契约与安全编码实践

C语言strcmp模拟实现:底层内存契约与安全编码实践

1. 为什么“模拟实现strcmp”是C语言学习路上绕不开的硬核关卡刚接触C语言字符串处理时,我见过太多人对着strcmp函数文档发呆:它返回一个整数,但这个整数到底代表什么?为什么不是简单的true/false?为什么比较两个相同字…

2026/8/27 21:12:25 阅读更多 →
Python 机器学习库 Top 10,你值得拥有!

Python 机器学习库 Top 10,你值得拥有!

随着人工智能技术发展且普及开来, 它超越了好多其他编程语言, 变成了机器学习领域里极热门且极常用的编程语言当中的一个。致使它在众多开发者里这般受追捧存在好多缘由, 其中一个便是它有着大量跟机器学习相关的开源框架以及工具库。依据它的数据表明, 45%的科技公司都趋向于把…

2026/8/27 21:11:25 阅读更多 →

日新闻

Go语言构建企业级AI服务网关:统一管理英伟达等AI接口调用

Go语言构建企业级AI服务网关:统一管理英伟达等AI接口调用

1. 项目概述:从零构建一个企业级的AI服务网关 最近在帮一个做内容审核的团队做技术架构升级,他们原来的业务里,每天有几十万张图片和短视频需要过审,最初是接了几个开源的AI模型自己部署,但效果和性能一直不太稳定。后…

2026/8/27 0:00:51 阅读更多 →
网盘直链下载助手5分钟解析八大网盘真实地址

网盘直链下载助手5分钟解析八大网盘真实地址

网盘直链下载助手5分钟解析八大网盘真实地址 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 夸…

2026/8/27 1:06:27 阅读更多 →
从零点亮 ESP32:Arduino ESP32 开发环境搭建与首次烧录完整指南

从零点亮 ESP32:Arduino ESP32 开发环境搭建与首次烧录完整指南

从零点亮 ESP32:Arduino ESP32 开发环境搭建与首次烧录完整指南 【免费下载链接】arduino-esp32 Arduino core for the ESP32 family of SoCs 项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32 Arduino ESP32 是乐鑫官方的 ESP32 系列 Ardui…

2026/8/27 1:06:27 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/26 14:45:33 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/26 17:46:43 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 14:46:37 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/27 17:46:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/26 17:46:39 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/27 20:00:17 阅读更多 →