搜尋
首頁資料庫mysql教程Red/System编译器实现分析(2)
Red/System编译器实现分析(2)Jun 07, 2016 pm 03:18 PM
redsystem分析如何實現開始編譯器講解

在开始讲解如何生成机器代码之前,我们先认识一些重要的数据结构: -- job ; 每个文件对应一个job对象,该对象会在整个流程各个步骤间传递。 job-class: context [ format: ;-- PE | ELF | Mach-o type: ;-- exe | obj | lib | dll target: ;-- CPU identifi

在开始讲解如何生成机器代码之前,我们先认识一些重要的数据结构:

-- job                          ; 每个文件对应一个job对象,该对象会在整个流程各个步骤间传递。

   job-class: context [
       format:                  ;-- 'PE | 'ELF | 'Mach-o
       type:                    ;-- 'exe | 'obj | 'lib | 'dll
       target:                  ;-- CPU identifier
       divs:                ;-- code/data divs
       flags:                   ;-- global flags
       sub-system:              ;-- target environment (GUI | console)
       symbols:                 ;-- symbols table
       buffer: none
   ]

-- globals                      ; 全局名字空间
-- locals                       ; 局部名字空间,比如函数内部

   locals:  none
   globals: make hash! 40       ;-- [name [type]]

-- code-buf                     ; 存放代码,对应PE文件的代码节,二进制格式存放
-- data-buf                     ; 存放全局变量,对应PE文件的数据节,二进制格式存放
-- symbols                      ; 这个就是符号表了,emitter和job引用同一个symbols table

   code-buf: make binary! 10'000
   data-buf: make binary! 10'000
   symbols:  make hash! 200     ;-- [name [type address [relocs]] ...]
上篇文章讲到函数 comp-expression,那就继续吧。
comp-expression expr                              ;将expr展开,comp-expression [a: 1]

comp-expression: func [tree /local name value][   ; tree? 没错,程序的结构本质上是一棵树
    switch/default type?/word tree/1 [
        set-word! [
            name: to-word tree/1                  ; name: a
            value: either block? tree/2 [         ; value: 1
                comp-expression tree/2
                'last
            ][
                tree/2
            ]
            add-symbol name value                 ; 将变量 a 放入符号表
            ...
            emitter/target/emit-store name value  ; 生成机器码
        ]
        ...
    ][...]
]
看看在函数 add-symbol 中做了些什么?
; add-symbol 'a 1

add-symbol: func [name [word!] value /local type new ctx][
    ctx: any [locals globals]                            ; 在全局名字空间里,ctx: globals
    unless find ctx name [
        type: case [                  ; type: integer!         
            ...
            'else [type?/word value]  ; value: 1
        ]           
        append ctx new: reduce [name compose [(type)]]   ; append ctx [a [integer!]]
        if ctx = globals [emitter/set-global new value]  ; 跟进函数 
                                                           emitter/set-global
    ]
]

; set-global [a [integer!]] 1

set-global: func [spec [block!] value /local type base][
    either 'struct! = type: spec/2/1 [                   ; spec/2/1: integer!
        ...
    ][
        base: tail data-buf
        store-global value select datatypes type         ; 最后一个函数了,坚持住!
    ]

    spec: reduce [spec/1 reduce ['global (index? base) - 1 make block! 5]] ;-- zero-based

    ; spec最终的结果是什么?
    ; 因为 a 是第一个变量,所以开始于 data-buf 的第 0 个字节处
    ; spec: [a [global 0 []]
    append symbols new-line spec yes
    spec
]

datatypes: to-hash [
    int8!       1   signed
    int16!      2   signed
    int32!      4   signed
    integer!    4   signed          ; select datatypes type "type" 为 integer!
    int64!      8   signed
    ...
]

; store-global 1 4

; 这函数的职责是将数据存放到 data-buf 中。
; 比如一个整数值为:0x08040201 (十六进制表示)
; 存放在内存中有两种形式:little-endian 和 big-endian
; 存放成哪种形式是由系统架构决定的,x86使用的是little-endian
; 所以要按照如下形式存放:0x01020408

store-global: func [value size /local ptr][
   ; 算法细节就不细说了。
   ; 好吧,算我偷懒 ;-)
]
函数 add-symbol 执行结束,做的事情还不少呢。总结一下:
  • 将变量放入符号表。此时符号表内容为 symbols: [ [a [global 0 []] ]
  • 将变量放入全局名字空间。此时 globals: [ [a [integer!]] ]
  • 将变量 a 的值 1 存入 data-buf。此时 data-buf: #{01000000}

可以看出 add-symbol 并不是一个’好‘函数,一个’好‘的函数职责应该是单一的。不过这是正常的,每个程序员在快速实现软件功能的阶段,都或多或少会写一些这样的代码。但一个优秀的程序员会在以后的迭代中不断改善,去掉这些坏味道。

函数add-symbol返回后,看看comp-expression,只剩下一行代码了,:- ) 这一行代码目的的机器码生成。

emitter/target/emit-store name value  ; emit-store 'a 1

; 目前只实现了IA32目标代码的生成
; target: do %targets/IA32.r
; 函数 emit-store 在文件 IA32.r 中

emit-store: func [name [word!] value [integer! word! string! struct!] /local spec][
    ...
    switch type?/word value [
        integer! [
            emit-variable name
                #{C705}                      ;-- gcode: MOV [name], value   ; (32-bit only!!!)
                #{C745}                      ;-- lcode: MOV [ebp+n], value  ; (32-bit only!!!)               
            emit to-bin32 value
        ]
        ...
    ]
]

emit-variable: func [
    name [word!] gcode [binary!] lcode [binary! block!] 
    /local offset
][
    ...
    
    ;-- global variable case
    emit gcode
    emit-reloc-addr emitter/symbols/:name    ; emit-reloc-addr [a [global 0 []]
]

emit-reloc-addr: func [spec [block!]][
    append spec/3 emitter/tail-ptr           ;-- 注意这里保存重定位的地址
    emit void-ptr                            ;-- emit void addr #{00000000}, reloc later
    ...
]

emit: func [bin [binary! char! block!]][
    append emitter/code-buf bin
]

emitter部分的代码本身不复杂,但要看懂需要有一定的x86汇编语言编程基础。汇编指令对应的机器指令可参考《英特尔? 64 和 IA-32 架构开发人员手册》。结果如下

; 将 1 存放到内存地址 00000000 处。
; 目前不确定数据段(data-buf)中的变量 a 相对于exe文件开头的位置
; 这个位置要到最后生成exe文件时,才能确定。
; 所以使用空指针占位
; code-buf中内容,注意值 1 按照little-endian格式存放
#{C7050000000001000000}       ;-- MOV [00000000], 1


; 符号表更新,加入了重定位的地址
; 也就是占位空指针的起始位置,zero-based
symbols: [ [a [global 0 [2]] ]   ;-- 占位空指针开始于第二个字节处
编译器是直接将代码翻译成机器码的,没有像编译原理教程上所说的先生成中间代码,再把中间代码翻译成机器码。直接生成机器码的好处是能够以最快速度的实现编译器,缺点是没法进行有力的优化。不知道大家发现没有,其实我们生产的这一段代码就是多余的。 ; -)  现阶段Red/System的目的是功能的完成,性能不是考虑的重点,所以没有使用中间代码。一但Red完成,使用Red重写Red/System的时候会引入中间代码,从而可以进行各种优化,使Red/System编译生成的程序达到C语言级别的速度。

到目前为止,Compiling部分已经完成。经典的编译原理课程一般到这里为止。接下来的一步称为Linking,也就是将我们的编译结果按照操作系统要求的格式拼装成文件,以便操作系统执行。Windows上使用的是 PE Format (Specification下载), Linux上使用的是 ELF Format (Specification下载)。网络上很多分析 PE 文件格式的文章,基本上都是在Microsoft公开 PE 文件格式之前,大牛们通过逆向工程得到的成果。这里向前辈们表示敬意!现在Microsoft已经公开的详细的文档,强烈建议阅读官方文档。

数据和代码都在data-buf和code-buf中准备好了,拼装成的PE文件格式如下:

    +-------------------+
    | DOS-stub          |
    +-------------------+
    | file-header       |
    +-------------------+
    | optional header   | 
<p>当所有文件头(DOS-stub,file-header,optional header和div headers)都生成好以后,code div和data div的相对于文件起始处的偏移地址也就确定了。这时可以将原来预留在code-buf中的占位空指针替换为数据段中变量实际的地址,这个地址是相对于文件起始处的偏移量。函数’resolve-data-refs‘用于完成这个工作。要完成这项工作需要三个结构 data-buf, code-buf 和 symbols。</p>
<p>结构 optional header 中包含一个成员 AddressOfEntryPoint,是程序的入口点地址。当Windows系统加载可执行文件的时候,会读取 AddressOfEntryPoint 中的内容,然后跳转的这个地址,开始运行程序。因为我们的代码放在div 1,所以我们把 AddressOfEntryPoint 设置成div 1的地址。</p>
<p>整个编译的过程完成了,是不是比想象中的要简单。: -)  当然了,之所以简单是因为我们的编译的程序几乎什么都没做。先对流程有一个总体的认识,能增加深入下去的信心。接下来会讲解稍复杂的部分:控制结构(if, while)以及函数。敬请期待!</p>


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
FiiO CP13 cassette player launches with transparent retro lookFiiO CP13 cassette player launches with transparent retro lookJun 16, 2024 am 09:52 AM

TheFiiOCP13cassetteplayerwasannouncedinJanuary.Now,FiiOisexpandingitsportfoliowithtwonewmodels-onewitharedfrontandonewithatransparentfront.Thelatternotonlyperfectlymatchestheretrocharmoftheangulardesign,butalso

区块链资料分析工具有哪些?区块链资料分析工具有哪些?Feb 21, 2025 pm 10:24 PM

区块链技术的迅速发展带来了对可靠且高效的分析工具的需求。这些工具对于从区块链交易中提取有价值的见解至关重要,以便更好地理解和利用其潜力。本文将探讨市场上一些领先的区块链数据分析工具,包括他们的功能、优势和局限性。通过了解这些工具,用户可以获得必要的见解,最大限度地利用区块链技术的可能性。

币安Launchpool第64期项目分析与价格预测:RedStone (RED)能否点燃市场?币安Launchpool第64期项目分析与价格预测:RedStone (RED)能否点燃市场?Mar 03, 2025 pm 11:45 PM

大家好,我是你们的老朋友,一个常年在币安广场和大家聊加密市场的小伙伴。币安Launchpool最近上线了第64期项目——RedStone(RED),作为一个多链预言机项目,它在上线前就引发了不少讨论。今天我们就来深度剖析一下RED,看看它的潜力如何,以及上线后的价格可能怎么走。币安Launchpool第64期项目分析与价格预测:RedStone(RED)能否点燃市场?项目背景与核心亮点RedStone(RED)是一个专注于去中心化金融(DeFi)的多链预言机平台,目标是为E

尼康官宣完成对电影摄影机 RED 公司的收购尼康官宣完成对电影摄影机 RED 公司的收购Apr 13, 2024 pm 09:22 PM

本站4月13日消息,日前,尼康官宣已完成RED公司的收购,RED正式成为尼康子公司。根据官方公告,RED原总裁JarredLand和创始人JamesJannard将作为新公司顾问,任命尼康影像事业部KeijiOishi为RED新公司CEO,RED公司原执行副总裁TommyRios升任新公司联席CEO。公告称,RED目前的产品阵容、合作伙伴以及与经销商的关系不会有任何变化。RED将继续通过保修、维修服务、客户服务和整体产品支持政策。尼康表示,尼康和RED将合并两家公司的优势,开发独特的产品,同时利

赛道遇冷,垂直领域的AI Agent能否打破僵局?赛道遇冷,垂直领域的AI Agent能否打破僵局?Mar 05, 2025 am 07:57 AM

Web3垂直AIAgent:颠覆传统,重塑行业格局?本文探讨了Web2和Web3中AIAgent的应用差异及Web3Agent的未来潜力。Web2已广泛应用AIAgent提升效率,涵盖销售、营销等领域,并取得显著经济效益。而Web3Agent则结合区块链技术,开辟了全新应用场景,尤其在DeFi领域。其通过代币激励、去中心化平台和链上数据分析,展现出超越Web2Agent的潜力。尽管Web3Agent目前面临挑战,但其独特优势使其在中长期有望与Web2竞争,甚至重塑行业格局。Web2AI

狗狗币etf价格会上涨多少倍狗狗币etf价格会上涨多少倍Mar 28, 2025 pm 03:42 PM

狗狗币ETF获批后可能的价格涨幅为2倍至5倍,现价0.18美元可能涨至0.6至1.2美元。1)乐观情景下,涨幅可达3倍至10倍,因市场牛市和马斯克助推;2)中性情景下,涨幅为1.5倍至3倍,因温和资金流入;3)悲观情景下,涨幅为0.5倍至1.5倍,因熊市和低流动性。

苹果M1芯片Mac上编译安装Redis失败,如何排查PHP7.3编译错误?苹果M1芯片Mac上编译安装Redis失败,如何排查PHP7.3编译错误?Mar 31, 2025 pm 11:39 PM

在苹果M1芯片Mac上编译安装Redis遇到的问题及解决方法许多用户在使用苹果M1芯片的Mac电脑编译安装Redis时,可能�...

如何在前台触发后台异步批量发送短信而不影响用户体验?如何在前台触发后台异步批量发送短信而不影响用户体验?Mar 31, 2025 pm 11:45 PM

如何实现前台触发后台异步批量发送短信的功能?在某些应用场景中,用户需要通过前台操作触发后台的批量短...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境