百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

链接器是如何一步步发明出来的?_如何使用连接器

myzbx 2025-09-03 05:31 55 浏览


在计算机编程的早期年代,你面临一个挥之不去的的噩梦。。。

你找了一个刚刚运行成功的程序仔细看了看:

; main.asm - 主程序start:    ; 初始化    mov cx, 10    mov dx, 20    ; 调用math.asm中的add函数    call 0x1234  ; 这里的0x1234是add函数在内存中的绝对地址    ; 退出    mov ax, 0    int 21h
; math.asm - 数学函数模块add:    ; 加法函数    mov ax, cx    add ax, dx    ret

你一眼就注意到main.asm中的那些数字了,0x12340x5678

这些是函数在最终内存中的绝对地址,也是所有程序员的噩梦,因为这些地址都是程序员手动计算出来的!

例如,如果math.asm被加载到内存地址0x1000,而add函数在模块内的偏移是0x234,那么add的绝对地址就是0x1234

这个过程不仅繁琐,而且极易出错,更糟糕的是维护问题

你清楚地知道,如果程序员在math.asm的开头添加了一个新函数,会发生什么!

; math.asm - 修改后new_function:  ; 新增的函数    ; 一些代码    retadd:  ; 位置改变了!    mov ax, cx    add ax, dx    ret

这个看似无害的修改会导致add函数的位置发生变化!它的偏移量增加了,绝对地址也随之改变。现在,main.asm中的call 0x1234指令将跳转到错误的位置!

程序员必须重新计算add函数的新地址并修改所有调用add的地方。

如果程序有数十个模块,数百个函数调用,这个过程将变成一场噩梦,每次修改代码,都可能引发一连串的地址更新工作

于是你的开始思索,需要一种机制,能够自动处理这些地址绑定,让程序员们专注于代码逻辑而非地址计算,为实现这种机制就决不能在程序中使用绝对内存地址!

不使用内存地址使用啥呢?

此时你想到当你找喊一个人的时候直呼其名而不是喊这个人的经纬度坐标,对了!这里也可以使用名字而不是地址来引用函数和变量,想到这里符号(Symbol)概念诞生了。

是啊,为啥要用内存地址硬编码,程序员可以使用符号啊:

; main.asm - 使用符号名start:    ; 初始化    mov cx, 10    mov dx, 20    ; 使用符号名而非硬编码地址    call add      ; 使用符号名"add"而非0x1234    ; 退出    mov ax, 0    int 21h

这种方法的核心思想是:程序员只需关心名字(如addprint),而不必关心这些函数最终在内存中的确切位置。

这是一个巨大的抽象飞跃!

你设计的符号概念带来了2个关键优势:

  1. 减少错误:不再需要手动计算和更新地址,消除了一大类潜在错误。

  2. 简化维护:当函数位置变化时,只需保持符号名不变,调用代码无需修改。

最重要的是,符号为自动化解决依赖关系奠定了基础。

符号概念是很优雅,但问题是:如何确定符号名最终的内存地址呢?

显然这次需要有一个能够自动确定符号最终内存地址的工具,让程序员彻底摆脱地址计算的负担,到底该怎么做到呢?

要达到这个目的就不能让编译器直接生成机器码,而是把这个过程拆成两步:

  • 编译器处理各个模块,但不必关心跨模块引用

  • 根据各个模块提供的信息来确定符号最终的内存地址并合并所有的模块为一个最终可执行文件

就这样在你的设想中你把整个编程过程拆成了两步,第一步是编译、第二步你将其称之为链接,link。

第二步中各个模块提供的信息还是比较模糊,这个信息是什么,该怎么提供?

既然编译器不直接生成最终的机器码,那么就需要一种文件来承接这一阶段编译器的输出,这个用来记录编译器第一阶段输出的文件就是所谓的目标文件,Object File。

这个文件包含机器码,但不去确定引用的外部符号的内存地址:

call print

你把所有这样的符号收集起来记录来目标文件中,这就是所谓的重定位表(Relocation Table),标记代码中需要在链接时填充正确地址的位置,这就是所谓的重新定位,重定位。

同时这个文件记录模块定义的所有符号(函数、变量)及其相对位置,这就是所谓符号表(Symbol Table):记录模块定义的所有符号(函数、变量)及其相对位置。

它们可能长这样:

-- main.obj --代码段:  偏移 0x03: mov dx, 20  偏移 0x06: call ???  (需要重定位,调用add)  偏移 0x0B: mov bx, ax  偏移 0x0D: call ???  (需要重定位,调用print)符号表:  start -> 偏移 0x00 (本模块定义,"我能提供什么")重定位表:  偏移 0x07: 需要add的地址    偏移 0x0E: 需要print的地址未解析引用:  add   (外部符号,“我需要什么”)print (外部符号,“我需要什么”)

目标文件的出现是一个关键突破,因为它:

  1. 分离了编译和链接:编译器只需关注单个模块的翻译,不必处理跨模块引用。

  2. 明确记录了依赖关系:每个模块清楚地表达了"我提供什么"(符号表)和"我需要什么"(未解析引用)。

  3. 为自动化链接提供了数据结构:重定位表明确标记了需要修正的地址位置。

现在, 你的任务就变得明确了:读取多个目标文件,解析它们的符号和依赖关系,然后将它们正确地"链接"在一起。

但如何实现这个链接过程?很明显,你需要实现两个核心算法:符号解析和重定位。

符号解析解决一个基本问题:将每个模块的"需求"与其他模块的"供给"匹配起来。

具体来说,你需要:

  1. 收集所有符号:遍历每个目标文件的符号表,建立一个全局符号字典,记录每个符号的定义位置。

  2. 检查未解析引用:对每个模块的未解析引用,在全局符号字典中查找其定义。

  3. 处理冲突和错误:如果一个符号有多个定义(冲突)或没有定义(未解析),生成适当的错误信息。

如果所有未解析引用都能在全局符号表中找到对应的定义,符号解析就成功了。否则,你的算法会生成一个错误,这就是后来的程序员熟悉的"undefined reference to..."。

符号解析解决了"符号供需匹配"问题,重定位的任务是:确定每个模块和符号在最终内存中的确切位置。

重定位过程包括:

  1. 内存布局规划:决定各个模块在最终内存空间中的排列顺序和基址。

  2. 地址计算:根据模块基址和符号在模块内的偏移,计算每个符号的最终绝对地址。

  3. 填充重定位条目:遍历每个模块的重定位表,将正确的地址填充到代码中的相应位置。

符号解析和重定位这两个步骤解决了模块化编程中最核心的问题:如何让分散在不同文件中的代码片段正确地找到并调用彼此。

至此,这两个核心算法的实现彻底解放了程序员,让他们不再需要手动计算和修改地址。


来源:码农的荒岛求生

编辑:未


转载内容仅代表作者观点

不代表中科院物理所立场

如需转载请联系原公众号

相关推荐

如何设计一个优秀的电子商务产品详情页

加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...

怎么在JS中使用Ajax进行异步请求?

大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...

中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革

前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...

前端监控 SDK 开发分享_前端监控系统 开源

一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...

Ajax 会被 fetch 取代吗?Axios 怎么办?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...

前端面试题《AJAX》_前端面试ajax考点汇总

1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...

Ajax 详细介绍_ajax

1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...

6款可替代dreamweaver的工具_替代powerdesigner的工具

dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

福斯《死侍》发布新剧照 "小贱贱"韦德被改造前造型曝光

时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...

不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!

Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...

2025 年 Python 爬虫四大前沿技术:从异步到 AI

作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...

最贱超级英雄《死侍》来了!_死侍超燃

死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...

停止javascript的ajax请求,取消axios请求,取消reactfetch请求

一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...