|
|
<!DOCTYPE html>
|
|
|
<html lang="zh-cn" dir="ltr">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<meta name="description" content="Go汇编 # 本节将介绍Go语言所使用到的汇编知识。在介绍Go汇编之前,我们先了解一些汇编语言,寄存器, AT&T 汇编语法,内存布局等前置知识点。这些知识点与Go汇编或多或少有关系,了解这些才能更好的帮助我们去看懂Go汇编代码。
|
|
|
前置知识 # 机器语言 # 机器语言是机器指令的集合。计算机的机器指令是一系列二进制数字。计算机将之转换为一系列高低电平脉冲信号来驱动硬件工作的。
|
|
|
汇编语言 # 机器指令是由0和1组成的二进制指令,难以编写与记忆。汇编语言是二进制指令的文本形式,与机器指令一一对应,相当于机器指令的助记码。比如,加法的机器指令是00000011写成汇编语言就是ADD。汇编的指令格式由操作码和操作数组成。
|
|
|
将助记码标准化后称为assembly language,缩写为asm,中文译为汇编语言。
|
|
|
汇编语言大致可以分为两类:
|
|
|
基于x86架构处理器的汇编语言
|
|
|
Intel 汇编 DOS(8086处理器), Windows Windows 派系 -> VC 编译器 AT&T 汇编 Linux, Unix, Mac OS, iOS(模拟器) Unix派系 -> GCC编译器 基于ARM 架构处理器的汇编语言
|
|
|
ARM 汇编 数据单元大小 # 汇编中数据单元大小可分为:
|
|
|
位 bit 半字节 Nibble 字节 Byte 字 Word 相当于两个字节 双字 Double Word 相当于2个字,4个字节 四字 Quadword 相当于4个字,8个字节 寄存器 # 寄存器是CPU中存储数据的器件,起到数据缓存作用。内存按照内存层级(memory hierarchy)依次分为寄存器,L1 Cache, L2 Cache, L3 Cache,其读写延迟依次增加,实现成本依次降低。
|
|
|
内存层级结构 寄存器分类 # 一个CPU中有多个寄存器。每一个寄存器都有自己的名称。寄存器按照种类分为通用寄存器和控制寄存器。其中通用寄存器有可细分为数据寄存器,指针寄存器,以及变址寄存器。">
|
|
|
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
|
|
|
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#343a40">
|
|
|
<meta name="color-scheme" content="light dark"><meta property="og:title" content="Go汇编语法" />
|
|
|
<meta property="og:description" content="Go汇编 # 本节将介绍Go语言所使用到的汇编知识。在介绍Go汇编之前,我们先了解一些汇编语言,寄存器, AT&T 汇编语法,内存布局等前置知识点。这些知识点与Go汇编或多或少有关系,了解这些才能更好的帮助我们去看懂Go汇编代码。
|
|
|
前置知识 # 机器语言 # 机器语言是机器指令的集合。计算机的机器指令是一系列二进制数字。计算机将之转换为一系列高低电平脉冲信号来驱动硬件工作的。
|
|
|
汇编语言 # 机器指令是由0和1组成的二进制指令,难以编写与记忆。汇编语言是二进制指令的文本形式,与机器指令一一对应,相当于机器指令的助记码。比如,加法的机器指令是00000011写成汇编语言就是ADD。汇编的指令格式由操作码和操作数组成。
|
|
|
将助记码标准化后称为assembly language,缩写为asm,中文译为汇编语言。
|
|
|
汇编语言大致可以分为两类:
|
|
|
基于x86架构处理器的汇编语言
|
|
|
Intel 汇编 DOS(8086处理器), Windows Windows 派系 -> VC 编译器 AT&T 汇编 Linux, Unix, Mac OS, iOS(模拟器) Unix派系 -> GCC编译器 基于ARM 架构处理器的汇编语言
|
|
|
ARM 汇编 数据单元大小 # 汇编中数据单元大小可分为:
|
|
|
位 bit 半字节 Nibble 字节 Byte 字 Word 相当于两个字节 双字 Double Word 相当于2个字,4个字节 四字 Quadword 相当于4个字,8个字节 寄存器 # 寄存器是CPU中存储数据的器件,起到数据缓存作用。内存按照内存层级(memory hierarchy)依次分为寄存器,L1 Cache, L2 Cache, L3 Cache,其读写延迟依次增加,实现成本依次降低。
|
|
|
内存层级结构 寄存器分类 # 一个CPU中有多个寄存器。每一个寄存器都有自己的名称。寄存器按照种类分为通用寄存器和控制寄存器。其中通用寄存器有可细分为数据寄存器,指针寄存器,以及变址寄存器。" />
|
|
|
<meta property="og:type" content="article" />
|
|
|
<meta property="og:url" content="https://go.cyub.vip/go-assembly/" /><meta property="article:section" content="" />
|
|
|
|
|
|
|
|
|
<title>Go汇编语法 | 深入Go语言之旅</title>
|
|
|
<link rel="manifest" href="/manifest.json">
|
|
|
<link rel="icon" href="/favicon.png" >
|
|
|
<link rel="stylesheet" href="/book.min.f06572240ce28e67eb332ac5cf5d59a696c47ad4c6f700d5842c5ed93dd8ec77.css" integrity="sha256-8GVyJAzijmfrMyrFz11ZppbEetTG9wDVhCxe2T3Y7Hc=" crossorigin="anonymous">
|
|
|
<script defer src="/flexsearch.min.js"></script>
|
|
|
<script defer src="/en.search.min.93e09a692e83129c8b0cc95c521eab7d4f5256b35651cf852c4ad4185bdc55a5.js" integrity="sha256-k+CaaS6DEpyLDMlcUh6rfU9SVrNWUc+FLErUGFvcVaU=" crossorigin="anonymous"></script>
|
|
|
|
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-BQ229RRTTX"></script>
|
|
|
<script>
|
|
|
var doNotTrack = false;
|
|
|
if (!doNotTrack) {
|
|
|
window.dataLayer = window.dataLayer || [];
|
|
|
function gtag(){dataLayer.push(arguments);}
|
|
|
gtag('js', new Date());
|
|
|
gtag('config', 'G-BQ229RRTTX', { 'anonymize_ip': false });
|
|
|
}
|
|
|
</script>
|
|
|
<!--
|
|
|
Made with Book Theme
|
|
|
https://github.com/alex-shpak/hugo-book
|
|
|
-->
|
|
|
|
|
|
</head>
|
|
|
<body dir="ltr">
|
|
|
<input type="checkbox" class="hidden toggle" id="menu-control" />
|
|
|
<input type="checkbox" class="hidden toggle" id="toc-control" />
|
|
|
<main class="container flex">
|
|
|
<aside class="book-menu">
|
|
|
<div class="book-menu-content">
|
|
|
|
|
|
<nav>
|
|
|
<h2 class="book-brand">
|
|
|
<a class="flex align-center" href="/"><img src="https://static.cyub.vip/images/202310/golang-480.png" alt="Logo" /><span>深入Go语言之旅</span>
|
|
|
</a>
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
<div class="book-search">
|
|
|
<input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" />
|
|
|
<div class="book-search-spinner hidden"></div>
|
|
|
<ul id="book-search-results"></ul>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
|
|
<li>
|
|
|
<a href="https://www.cyub.vip/" target="_blank" rel="noopener">
|
|
|
个人博客
|
|
|
</a>
|
|
|
</li>
|
|
|
|
|
|
<li>
|
|
|
<a href="https://github.com/cyub" target="_blank" rel="noopener">
|
|
|
Github主页
|
|
|
</a>
|
|
|
</li>
|
|
|
|
|
|
<li>
|
|
|
<a href="https://www.topgoer.cn/?ref=go.cyub.vip" target="_blank" rel="noopener">
|
|
|
地鼠文档
|
|
|
</a>
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<ul>
|
|
|
<li>
|
|
|
<p><strong>
|
|
|
<a href="/">深入Go语言之旅</a></strong></p>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>准备篇</strong></p>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/compiler/">编译流程</a></li>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/">分析工具</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/gdb/">GDB</a></li>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/dlv/">Delve</a></li>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/go-buildin-tools/">Go 内置工具</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/go-assembly/"class=active>Go汇编</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>基础篇</strong></p>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/type/">数据类型与数据结构</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/type/string/">字符串</a></li>
|
|
|
<li>
|
|
|
<a href="/type/array/">数组</a></li>
|
|
|
<li>
|
|
|
<a href="/type/slice/">切片</a></li>
|
|
|
<li>
|
|
|
<a href="/type/nil/">nil</a></li>
|
|
|
<li>
|
|
|
<a href="/type/empty_struct/">空结构体</a></li>
|
|
|
<li>
|
|
|
<a href="/type/pointer/">指针</a></li>
|
|
|
<li>
|
|
|
<a href="/type/map/">映射</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/function/">函数</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/function/first-class/">一等公民</a></li>
|
|
|
<li>
|
|
|
<a href="/function/call-stack/">函数调用栈</a></li>
|
|
|
<li>
|
|
|
<a href="/function/pass-by-value/">值传递</a></li>
|
|
|
<li>
|
|
|
<a href="/function/closure/">闭包</a></li>
|
|
|
<li>
|
|
|
<a href="/function/method/">方法</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/feature/">语言特性</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/feature/comma-ok/">逗号ok模式</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/for-range/">遍历 - for-range语法</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/defer/">延迟执行 - defer语法</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/select/">通道选择器 - select语法</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/panic-recover/">恐慌与恢复 - panic/recover</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>运行时篇</strong></p>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/concurrency/">并发编程</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/concurrency/memory-model/">内存模型</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/context/">上下文 - context</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/channel/">通道 - channel</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/atomic/">原子操作 - atomic</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-map/">并发Map - sync.Map</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-waitgroup/">等待组 - sync.WaitGroup</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-once/">一次性操作 - sync.Once</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-pool/">缓冲池 - sync.Pool</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-cond/">条件变量 - sync.Cond</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-mutex/">互斥锁 - sync.Mutex</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-rwmutex/">读写锁 - sync.RWMutex</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/gmp/">G-M-P调度机制</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/gmp/gmp-model/">调度机制概述</a></li>
|
|
|
<li>
|
|
|
<a href="/gmp/scheduler/">调度器</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/memory/">内存管理</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/memory/allocator/">内存分配器</a></li>
|
|
|
<li>
|
|
|
<a href="/memory/gc/">GC</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/type-system/">类型系统</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/type-system/type/">类型系统</a></li>
|
|
|
<li>
|
|
|
<a href="/type-system/interface/">接口</a></li>
|
|
|
<li>
|
|
|
<a href="/type-system/reflect/">反射</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
</aside>
|
|
|
|
|
|
<div class="book-page">
|
|
|
<header class="book-header">
|
|
|
|
|
|
<div class="flex align-center justify-between">
|
|
|
<label for="menu-control">
|
|
|
<img src="/svg/menu.svg" class="book-icon" alt="Menu" />
|
|
|
</label>
|
|
|
|
|
|
<strong>Go汇编语法</strong>
|
|
|
|
|
|
<label for="toc-control">
|
|
|
|
|
|
</label>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="markdown"><h1 id="go汇编">
|
|
|
Go汇编
|
|
|
<a class="anchor" href="#go%e6%b1%87%e7%bc%96">#</a>
|
|
|
</h1>
|
|
|
<p>本节将介绍Go语言所使用到的汇编知识。在介绍Go汇编之前,我们先了解一些汇编语言,寄存器, AT&T 汇编语法,内存布局等前置知识点。这些知识点与Go汇编或多或少有关系,了解这些才能更好的帮助我们去看懂Go汇编代码。</p>
|
|
|
<h2 id="前置知识">
|
|
|
前置知识
|
|
|
<a class="anchor" href="#%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%af%86">#</a>
|
|
|
</h2>
|
|
|
<h3 id="机器语言">
|
|
|
机器语言
|
|
|
<a class="anchor" href="#%e6%9c%ba%e5%99%a8%e8%af%ad%e8%a8%80">#</a>
|
|
|
</h3>
|
|
|
<p>机器语言是机器指令的集合。计算机的机器指令是一系列二进制数字。计算机将之转换为一系列高低电平脉冲信号来驱动硬件工作的。</p>
|
|
|
<h3 id="汇编语言">
|
|
|
汇编语言
|
|
|
<a class="anchor" href="#%e6%b1%87%e7%bc%96%e8%af%ad%e8%a8%80">#</a>
|
|
|
</h3>
|
|
|
<p>机器指令是由0和1组成的二进制指令,难以编写与记忆。汇编语言是二进制指令的文本形式,与机器指令一一对应,相当于机器指令的助记码。比如,加法的机器指令是<code>00000011</code>写成汇编语言就是<code>ADD</code>。<strong>汇编的指令格式由操作码和操作数组成</strong>。</p>
|
|
|
<p>将助记码标准化后称为<code>assembly language</code>,缩写为<code>asm</code>,中文译为汇编语言。</p>
|
|
|
<p>汇编语言大致可以分为两类:</p>
|
|
|
<ol>
|
|
|
<li>
|
|
|
<p>基于x86架构处理器的汇编语言</p>
|
|
|
<ul>
|
|
|
<li>Intel 汇编
|
|
|
<ul>
|
|
|
<li>DOS(8086处理器), Windows</li>
|
|
|
<li>Windows 派系 -> VC 编译器</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>AT&T 汇编
|
|
|
<ul>
|
|
|
<li>Linux, Unix, Mac OS, iOS(模拟器)</li>
|
|
|
<li>Unix派系 -> GCC编译器</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p>基于ARM 架构处理器的汇编语言</p>
|
|
|
<ul>
|
|
|
<li>ARM 汇编</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ol>
|
|
|
<h4 id="数据单元大小">
|
|
|
数据单元大小
|
|
|
<a class="anchor" href="#%e6%95%b0%e6%8d%ae%e5%8d%95%e5%85%83%e5%a4%a7%e5%b0%8f">#</a>
|
|
|
</h4>
|
|
|
<p>汇编中数据单元大小可分为:</p>
|
|
|
<ul>
|
|
|
<li>位 bit</li>
|
|
|
<li>半字节 Nibble</li>
|
|
|
<li>字节 Byte</li>
|
|
|
<li>字 Word 相当于两个字节</li>
|
|
|
<li>双字 Double Word 相当于2个字,4个字节</li>
|
|
|
<li>四字 Quadword 相当于4个字,8个字节</li>
|
|
|
</ul>
|
|
|
<h3 id="寄存器">
|
|
|
寄存器
|
|
|
<a class="anchor" href="#%e5%af%84%e5%ad%98%e5%99%a8">#</a>
|
|
|
</h3>
|
|
|
<p>寄存器是CPU中存储数据的器件,起到数据缓存作用。内存按照内存层级(memory hierarchy)依次分为寄存器,L1 Cache, L2 Cache, L3 Cache,其读写延迟依次增加,实现成本依次降低。</p>
|
|
|
<figure class="text-center"><img src="https://static.cyub.vip/images/202104/mem_arch.jpg" width="500px"/><figcaption>
|
|
|
<h4>内存层级结构</h4>
|
|
|
</figcaption>
|
|
|
</figure>
|
|
|
|
|
|
<h4 id="寄存器分类">
|
|
|
寄存器分类
|
|
|
<a class="anchor" href="#%e5%af%84%e5%ad%98%e5%99%a8%e5%88%86%e7%b1%bb">#</a>
|
|
|
</h4>
|
|
|
<p>一个CPU中有多个寄存器。每一个寄存器都有自己的名称。寄存器按照种类分为通用寄存器和控制寄存器。其中通用寄存器有可细分为数据寄存器,指针寄存器,以及变址寄存器。</p>
|
|
|
<p>
|
|
|
<img src="https://static.cyub.vip/images/202007/register.jpg" alt="" /></p>
|
|
|
<p>1979年因特尔推出8086架构的CPU,开始支持16位。为了兼容之前8008架构的8位CPU,8086架构中AX寄存器高8位称为AH,低8位称为AL,用来对应8008架构的8位的A寄存器。后来随着x86,以及x86-64
|
|
|
架构的CPU推出,开始支持32位以及64位,为了兼容并保留了旧名称,16位处理器的AX寄存器拓展成EAX(E代表拓展Extended的意思)。对于64位处理器的寄存器相应的RAX(R代表寄存器Register的意思)。其他指令也类似。</p>
|
|
|
<p>
|
|
|
<img src="https://static.cyub.vip/images/202102/rax.jpg" alt="" /></p>
|
|
|
<p>各个寄存器功能介绍:</p>
|
|
|
<table>
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>寄存器</th>
|
|
|
<th>功能</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
<tr>
|
|
|
<td><strong>AX</strong></td>
|
|
|
<td>A代表累加器Accumulator,X是八位寄存器AH和AL的中H和L的占位符,表示AX由AH和AL组成。AX一般用于算术与逻辑运算,以及作为函数返回值</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>BX</strong></td>
|
|
|
<td>B代表Base,BX一般用于保存中间地址(hold indirect addresses)</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>CX</strong></td>
|
|
|
<td>C代表Count,CX一般用于计数,比如使用它来计算循环中的迭代次数或指定字符串中的字符数</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>DX</strong></td>
|
|
|
<td>D代表Data,DX一般用于保存某些算术运算的溢出,并且在访问80x86 I/O总线上的数据时保存I/O地址</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>DI</strong></td>
|
|
|
<td>DI代表Destination Index,DI一般用于指针</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>SI</strong></td>
|
|
|
<td>SI代表Source Index,SI用途同DI一样</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>SP</strong></td>
|
|
|
<td>SP代表Stack Pointer,是栈指针寄存器,存放着执行函数对应栈帧的栈顶地址,且始终指向栈顶</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>BP</strong></td>
|
|
|
<td>BP代表Base Pointer,是栈帧基址指针寄存器,存放这执行函数对应栈帧的栈底地址,一般用于访问栈中的局部变量和参数</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td><strong>IP</strong></td>
|
|
|
<td>IP代表Instruction Pointer,是指令寄存器,指向处理器下条等待执行的指令地址(代码段内的偏移量),每次执行完相应汇编指令IP值就会增加;IP是个特殊寄存器,不能像访问通用寄存器那样访问它。IP可被jmp、call和ret等指令隐含地改变</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
<h3 id="进程在虚拟内存中布局">
|
|
|
进程在虚拟内存中布局
|
|
|
<a class="anchor" href="#%e8%bf%9b%e7%a8%8b%e5%9c%a8%e8%99%9a%e6%8b%9f%e5%86%85%e5%ad%98%e4%b8%ad%e5%b8%83%e5%b1%80">#</a>
|
|
|
</h3>
|
|
|
<p>32位系统下,虚拟内存空间大小为4G,每一个进程独立的运行在该虚拟内存空间上。从0x00000000开始的3G空间属于用户空间,剩下1G空间属于内核空间。</p>
|
|
|
<p>用户空间还可以进一步细分,每一部分叫做段(section),大致可以分为以下几段:</p>
|
|
|
<ul>
|
|
|
<li>Stack 栈空间:用于函数调用中存储局部变量、返回地址、返回值等,向下增长,变量存储和使用过程叫做入栈和出栈过程</li>
|
|
|
<li>Heap 堆空间:用于动态申请的内存,比如c语言通过malloc函数调用分配内存,其向上增长。指针型变量指向的一般就是这里面的空间。存储此空间的数据需要GC的。栈上变量scope是函数级的,而堆上变量属于进程级的</li>
|
|
|
<li>Bss段:未初始化数据区,存储未初始化的全局变量或静态变量</li>
|
|
|
<li>Data段:初始化数据区,存储已经初始化的全局变量或静态变量</li>
|
|
|
<li>Text段:代码区,存储的是源码编译后二进制指令</li>
|
|
|
</ul>
|
|
|
<p>
|
|
|
<img src="https://static.cyub.vip/images/202102/process_mem_layout.jpeg" alt="内存布局" /></p>
|
|
|
<p>在32位系统中进程空间(即用户空间)范围为<code>0x00000000 ~ 0xbfffffff</code>,内核空间范围为<code>0xc0000000 ~ 0xffffffff</code>, 实际上分配的进程空间并不是从0x00000000开始的,而是从0x08048000开始,到0xbfffffff结束。另外进程实际的esp指向的地址并不是从0xbfffffff开始的,因为linux系统会在程序初始化前,将一些命令行参数及环境变量以及ELF辅助向量(<code>ELF Auxiliary Vectors</code>)等信息放到栈上。进程启动时,其空间布局如下所示(注意图示中地址是从低地址到高地址的):</p>
|
|
|
<pre tabindex="0"><code>stack pointer -> [ argc = number of args ] 4
|
|
|
[ argv[0] (pointer) ] 4 (program name)
|
|
|
[ argv[1] (pointer) ] 4
|
|
|
[ argv[..] (pointer) ] 4 * x
|
|
|
[ argv[n - 1] (pointer) ] 4
|
|
|
[ argv[n] (pointer) ] 4 (= NULL)
|
|
|
|
|
|
[ envp[0] (pointer) ] 4
|
|
|
[ envp[1] (pointer) ] 4
|
|
|
[ envp[..] (pointer) ] 4
|
|
|
[ envp[term] (pointer) ] 4 (= NULL)
|
|
|
|
|
|
[ auxv[0] (Elf32_auxv_t) ] 8
|
|
|
[ auxv[1] (Elf32_auxv_t) ] 8
|
|
|
[ auxv[..] (Elf32_auxv_t) ] 8
|
|
|
[ auxv[term] (Elf32_auxv_t) ] 8 (= AT_NULL vector)
|
|
|
|
|
|
[ padding ] 0 - 16
|
|
|
|
|
|
[ argument ASCIIZ strings ] >= 0
|
|
|
[ environment ASCIIZ strings ] >= 0
|
|
|
[ program name ASCIIZ strings ] >= 0
|
|
|
|
|
|
(0xbffffffc) [ end marker ] 4 (= NULL)
|
|
|
|
|
|
(0xc0000000) < bottom of stack > 0 (virtual)
|
|
|
</code></pre><p>进程空间起始位置处存放命令行参数个数与参数信息,我们将在后面章节有讨论到。</p>
|
|
|
<h3 id="caller-与-callee">
|
|
|
caller 与 callee
|
|
|
<a class="anchor" href="#caller-%e4%b8%8e-callee">#</a>
|
|
|
</h3>
|
|
|
<p>如果一个函数调用另外一个函数,那么该函数被称为调用者函数,也叫做caller,而被调用的函数称为被调用者函数,也叫做callee。比如函数main中调用sum函数,那么main就是caller,而sum函数就是callee。</p>
|
|
|
<h3 id="栈帧">
|
|
|
栈帧
|
|
|
<a class="anchor" href="#%e6%a0%88%e5%b8%a7">#</a>
|
|
|
</h3>
|
|
|
<p>栈帧即stack frame,即未完成函数所持有的,独立连续的栈区域,用来保存其局部变量,返回地址等信息。</p>
|
|
|
<h3 id="函数栈">
|
|
|
函数栈
|
|
|
<a class="anchor" href="#%e5%87%bd%e6%95%b0%e6%a0%88">#</a>
|
|
|
</h3>
|
|
|
<p>当前函数作为caller,其本身拥有的栈帧以及其所有callee的栈帧,可以称为该函数的函数栈。一般情况下函数栈大小是固定的,如果超出栈空间,就会栈溢出异常。比如递归求斐波拉契,这时候可以使用尾调用来优化。用火焰图分析性能时候,火焰越高,说明栈越深。</p>
|
|
|
<h3 id="att-汇编语法">
|
|
|
AT&T 汇编语法
|
|
|
<a class="anchor" href="#att-%e6%b1%87%e7%bc%96%e8%af%ad%e6%b3%95">#</a>
|
|
|
</h3>
|
|
|
<p>AT&T汇编语法是类Unix的系统上的标准汇编语法,比如gcc、gdb中默认都是使用AT&T汇编语法。AT&T汇编的指令格式如下:</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>instruction src dst
|
|
|
</span></span></code></pre></div><p>其中<code>instruction</code>是指令助记符,也叫操作码,比如<code>mov</code>就是一个指令助记符,<code>src</code>是源操作数,<code>dst</code>是目的操作。</p>
|
|
|
<p>当引用寄存器时候,应在寄存器名称加前缀<code>%</code>,对于常数,则应加前缀 <strong>$</strong>。</p>
|
|
|
<h4 id="指令分类">
|
|
|
指令分类
|
|
|
<a class="anchor" href="#%e6%8c%87%e4%bb%a4%e5%88%86%e7%b1%bb">#</a>
|
|
|
</h4>
|
|
|
<h5 id="数据传输指令">
|
|
|
数据传输指令
|
|
|
<a class="anchor" href="#%e6%95%b0%e6%8d%ae%e4%bc%a0%e8%be%93%e6%8c%87%e4%bb%a4">#</a>
|
|
|
</h5>
|
|
|
<table>
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>汇编指令</th>
|
|
|
<th>逻辑表达式</th>
|
|
|
<th>含义</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
<tr>
|
|
|
<td>mov $0x05, %ax</td>
|
|
|
<td>R[ax] = 0x05</td>
|
|
|
<td>将数值5存储到寄存器ax中</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>mov %ax, -4(%bp)</td>
|
|
|
<td>mem[R[bp] -4] = R[ax]</td>
|
|
|
<td>将ax寄存器中存储的数据存储到<!-- raw HTML omitted --><strong>bp寄存器存的地址减去4之后的内存地址</strong>中,</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>mov -4(%bp), %ax</td>
|
|
|
<td>R[ax] = mem[R[bp] -4]</td>
|
|
|
<td>bp寄存器存储的地址减去4值,<!-- raw HTML omitted -->然后改地址对应的内存存储的信息存储到ax寄存器中</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>mov $0x10, (%sp)</td>
|
|
|
<td>mem[R[sp]] = 0x10</td>
|
|
|
<td>将16存储到sp寄存器存储的地址对应的内存</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>push $0x03</td>
|
|
|
<td>mem[R[sp]] = 0x03<!-- raw HTML omitted --> R[sp] = R[sp] - 4</td>
|
|
|
<td>将数值03入栈,然后sp寄存器存储的地址减去4</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>pop</td>
|
|
|
<td>R[sp] = R[sp] + 4</td>
|
|
|
<td>将当前sp寄存器指向的地址的变量出栈,<!-- raw HTML omitted -->并将sp寄存器存储的地址加4</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>call func1</td>
|
|
|
<td>—</td>
|
|
|
<td>调用函数func1</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>ret</td>
|
|
|
<td>—</td>
|
|
|
<td>函数返回,将返回值存储到寄存器中或caller栈中,<!-- raw HTML omitted -->并将return address弹出到ip寄存器中</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
<p>当使用<code>mov</code>指令传递数据时,数据的大小由mov指令的后缀决定。</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-as" data-lang="as"><span style="display:flex;"><span><span style="color:#a6e22e">movb</span> <span style="color:#a6e22e">$123</span><span style="color:#f92672">,</span> <span style="color:#f92672">%</span><span style="color:#a6e22e">eax</span> <span style="color:#75715e">// 1 byte
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">movw</span> <span style="color:#a6e22e">$123</span><span style="color:#f92672">,</span> <span style="color:#f92672">%</span><span style="color:#a6e22e">eax</span> <span style="color:#75715e">// 2 byte
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">movl</span> <span style="color:#a6e22e">$123</span><span style="color:#f92672">,</span> <span style="color:#f92672">%</span><span style="color:#a6e22e">eax</span> <span style="color:#75715e">// 4 byte
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">movq</span> <span style="color:#a6e22e">$123</span><span style="color:#f92672">,</span> <span style="color:#f92672">%</span><span style="color:#a6e22e">eax</span> <span style="color:#75715e">// 8 byte
|
|
|
</span></span></span></code></pre></div><h5 id="算术运算指令">
|
|
|
算术运算指令
|
|
|
<a class="anchor" href="#%e7%ae%97%e6%9c%af%e8%bf%90%e7%ae%97%e6%8c%87%e4%bb%a4">#</a>
|
|
|
</h5>
|
|
|
<table>
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>指令</th>
|
|
|
<th>含义</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
<tr>
|
|
|
<td>subl $0x05, %eax</td>
|
|
|
<td>R[eax] = R[eax] - 0x05</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>subl %eax, -4(%ebp)</td>
|
|
|
<td>mem[R[ebp] -4] = mem[R[ebp] -4] - R[eax]</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>subl -4(%ebp), %eax</td>
|
|
|
<td>R[eax] = R[eax] - mem[R[ebp] -4]</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
<h5 id="跳转指令">
|
|
|
跳转指令
|
|
|
<a class="anchor" href="#%e8%b7%b3%e8%bd%ac%e6%8c%87%e4%bb%a4">#</a>
|
|
|
</h5>
|
|
|
<table>
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>指令</th>
|
|
|
<th>含义</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
<tr>
|
|
|
<td>cmpl %eax %ebx</td>
|
|
|
<td>计算 R[eax] - R[ebx], 然后设置flags寄存器</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>jmp location</td>
|
|
|
<td>无条件跳转到location</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>je location</td>
|
|
|
<td>如果flags寄存器设置了相等标志,则跳转到location</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>jg, jge, jl, gle, jnz, … location</td>
|
|
|
<td>如果flags寄存器设置了>, >=, <, <=, != 0等标志,则跳转到location</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
<h5 id="栈与地址管理指令">
|
|
|
栈与地址管理指令
|
|
|
<a class="anchor" href="#%e6%a0%88%e4%b8%8e%e5%9c%b0%e5%9d%80%e7%ae%a1%e7%90%86%e6%8c%87%e4%bb%a4">#</a>
|
|
|
</h5>
|
|
|
<table>
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>指令</th>
|
|
|
<th>含义</th>
|
|
|
<th>等同操作</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
<tr>
|
|
|
<td>pushl %eax</td>
|
|
|
<td>将R[eax]入栈</td>
|
|
|
<td>subl $4, %esp; <!-- raw HTML omitted -->movl %eax, (%esp)</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>popl %eax</td>
|
|
|
<td>将栈顶数据弹出,然后存储到R[eax]</td>
|
|
|
<td>movl (%esp), %eax <!-- raw HTML omitted --> addl $4, %esp</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>leave</td>
|
|
|
<td>Restore the callers stack pointer</td>
|
|
|
<td>movl %ebp, %esp <!-- raw HTML omitted -->pop %ebp</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>lea 8(%esp), %esi</td>
|
|
|
<td>将R[esp]存放的地址加8,然后存储到R[esi]</td>
|
|
|
<td>R[esi] = R[esp] + 8</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
<p><strong>lea</strong> 是<code>load effective address</code>的缩写,用于将一个内存地址直接赋给目的操作数。</p>
|
|
|
<h5 id="函数调用指令">
|
|
|
函数调用指令
|
|
|
<a class="anchor" href="#%e5%87%bd%e6%95%b0%e8%b0%83%e7%94%a8%e6%8c%87%e4%bb%a4">#</a>
|
|
|
</h5>
|
|
|
<table>
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th>指令</th>
|
|
|
<th>含义</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
<tr>
|
|
|
<td>call label</td>
|
|
|
<td>调用函数,并将返回地址入栈</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>ret</td>
|
|
|
<td>从栈中弹出返回地址,并跳转至该返回地址</td>
|
|
|
</tr>
|
|
|
<tr>
|
|
|
<td>leave</td>
|
|
|
<td>恢复调用者者栈指针</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
<blockquote class="book-hint warning">
|
|
|
<p><strong>注意:</strong></p>
|
|
|
<p>以上指令分类并不规范和完整,比如 <code>call</code> , <code>ret</code> 都可以算作无条件跳转指令,这里面是按照功能放在函数调用这一分类了。</p>
|
|
|
|
|
|
</blockquote>
|
|
|
|
|
|
<h2 id="go-汇编">
|
|
|
Go 汇编
|
|
|
<a class="anchor" href="#go-%e6%b1%87%e7%bc%96">#</a>
|
|
|
</h2>
|
|
|
<p>Go语言汇编器采用Plan9 汇编语法,该汇编语言是由贝尔实验推出来的。下面说的Go汇编也就是Plan9 汇编。
|
|
|
不同于C语言汇编中汇编指令的寄存器都是代表硬件寄存器,Go汇编中的寄存器使用的是伪寄存器,可以把Go汇编考虑成是底层硬件汇编之上的抽象。</p>
|
|
|
<h3 id="伪寄存器">
|
|
|
伪寄存器
|
|
|
<a class="anchor" href="#%e4%bc%aa%e5%af%84%e5%ad%98%e5%99%a8">#</a>
|
|
|
</h3>
|
|
|
<p>Go汇编一共有4个伪寄存器:</p>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<p><strong>FP</strong>: Frame pointer: arguments and locals.</p>
|
|
|
<ul>
|
|
|
<li>使用形如 symbol+offset(FP) 的方式,引用函数的输入参数。例如 arg0+0(FP),arg1+8(FP)</li>
|
|
|
<li>offset是正值</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>PC</strong>: Program counter: jumps and branches.</p>
|
|
|
<ul>
|
|
|
<li>PC寄存器,在 x86 平台下对应 ip 寄存器,amd64 上则是 rip</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>SB</strong>: Static base pointer: global symbols.</p>
|
|
|
<ul>
|
|
|
<li>全局静态基指针,一般用来声明函数或全局变量</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>SP</strong>: Stack pointer: top of stack.</p>
|
|
|
<ul>
|
|
|
<li>SP寄存器指向当前栈帧的局部变量的开始位置,使用形如 symbol+offset(SP) 的方式,引用函数的局部变量。</li>
|
|
|
<li>offset是负值,offset 的合法取值是 [-framesize, 0)。</li>
|
|
|
<li>手写汇编代码时,如果是 symbol+offset(SP) 形式,则表示伪寄存器 SP。如果是 offset(SP) 则表示硬件寄存器 SP。<strong>对于编译输出(go tool compile -S / go tool objdump)的代码来讲,所有的 SP 都是硬件寄存器 SP,无论是否带 symbol</strong>。</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
<h3 id="函数声明">
|
|
|
函数声明
|
|
|
<a class="anchor" href="#%e5%87%bd%e6%95%b0%e5%a3%b0%e6%98%8e">#</a>
|
|
|
</h3>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span> 参数大小+返回值大小
|
|
|
</span></span><span style="display:flex;"><span> |
|
|
|
</span></span><span style="display:flex;"><span> TEXT pkgname·add<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>,NOSPLIT,$32-16
|
|
|
</span></span><span style="display:flex;"><span> | | |
|
|
|
</span></span><span style="display:flex;"><span> 包名 函数名 栈帧大小
|
|
|
</span></span></code></pre></div><ul>
|
|
|
<li>
|
|
|
<p><code>TEXT</code>指令声明了<code>pagname.add</code>是在<code>.text</code>段</p>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><code>pkgname·add</code>中的<code>·</code>,是一个 <code>unicode</code> 的中点。在程序被链接之后,所有的中点<code>·</code>都会被替换为点号<code>.</code>,所以通过 <strong>
|
|
|
<a href="/analysis-tools/gdb/">GDB</a></strong> 调试打断点时候,应该是 <code>b pagname.add</code></p>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><code>(SB)</code>: <code>SB</code> 是一个虚拟寄存器,保存了静态基地址(static-base) 指针,即我们程序地址空间的开始地址。 <code>"".add(SB)</code> 表明我们的符号add位于某个固定的相对地址空间起始处的偏移位置</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>objdump -j .text -t test | grep <span style="color:#e6db74">'main.add'</span> <span style="color:#75715e"># 可获得main.add的绝对地址</span>
|
|
|
</span></span></code></pre></div></li>
|
|
|
<li>
|
|
|
<p><code>NOSPLIT</code>: 表明该函数内部不进行栈分裂逻辑处理,可以避免CPU资源浪费。关于栈分裂会在调度器章节介绍</p>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><code>$32-16</code>: <code>$32</code>代表即将分配的栈帧大小;而<code>$16</code>指定了传入的参数与返回值的大小</p>
|
|
|
</li>
|
|
|
</ul>
|
|
|
<h4 id="函数调用栈">
|
|
|
函数调用栈
|
|
|
<a class="anchor" href="#%e5%87%bd%e6%95%b0%e8%b0%83%e7%94%a8%e6%a0%88">#</a>
|
|
|
</h4>
|
|
|
<p>Go汇编中函数调用的参数以及返回值都是由栈传递和保存的,这部分空间由<code>caller</code>在其栈帧(stack frame)上提供。Go汇编中没有使用PUSH/POP指令进行栈的伸缩处理,所有栈的增长和收缩是通过在栈指针寄存器<code>SP</code>上分别执行加减指令来实现的。</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> caller
|
|
|
</span></span><span style="display:flex;"><span> +------------------+
|
|
|
</span></span><span style="display:flex;"><span> | |
|
|
|
</span></span><span style="display:flex;"><span> +----------------------> |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | caller parent BP |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------| <--------- BP<span style="color:#f92672">(</span>pseudo SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> | | local Var0 |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | ......... |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | local VarN |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | temporarily |
|
|
|
</span></span><span style="display:flex;"><span> | unused space |
|
|
|
</span></span><span style="display:flex;"><span>caller stack frame |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | callee retN |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | ......... |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | callee ret0 |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | callee argN |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | ......... |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------|
|
|
|
</span></span><span style="display:flex;"><span> | | callee arg0 |
|
|
|
</span></span><span style="display:flex;"><span> | |------------------| <--------- FP<span style="color:#f92672">(</span>virtual register<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> | | <span style="color:#66d9ef">return</span> addr |
|
|
|
</span></span><span style="display:flex;"><span> +----------------------> |------------------| <----------------------+
|
|
|
</span></span><span style="display:flex;"><span> | caller BP | |
|
|
|
</span></span><span style="display:flex;"><span> BP<span style="color:#f92672">(</span>pseudo SP<span style="color:#f92672">)</span> ------> |------------------| |
|
|
|
</span></span><span style="display:flex;"><span> | local Var0 | |
|
|
|
</span></span><span style="display:flex;"><span> |------------------| |
|
|
|
</span></span><span style="display:flex;"><span> | local Var1 |
|
|
|
</span></span><span style="display:flex;"><span> |------------------| callee stack frame
|
|
|
</span></span><span style="display:flex;"><span> | ......... |
|
|
|
</span></span><span style="display:flex;"><span> |------------------| |
|
|
|
</span></span><span style="display:flex;"><span> | local VarN | |
|
|
|
</span></span><span style="display:flex;"><span> SP<span style="color:#f92672">(</span>Real Register<span style="color:#f92672">)</span> ------> |------------------| |
|
|
|
</span></span><span style="display:flex;"><span> | | |
|
|
|
</span></span><span style="display:flex;"><span> | | |
|
|
|
</span></span><span style="display:flex;"><span> +------------------+ <----------------------+
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> callee
|
|
|
</span></span></code></pre></div><p>关于Go汇编进一步知识,我们将在 《<strong>
|
|
|
<a href="/function/call-stack/">基础篇-函数-函数调用栈</a></strong> 》 章节详细探讨说明,此处我们只需要大致了解下函数声明、调用栈概念即可。</p>
|
|
|
<h3 id="获取go汇编代码">
|
|
|
获取Go汇编代码
|
|
|
<a class="anchor" href="#%e8%8e%b7%e5%8f%96go%e6%b1%87%e7%bc%96%e4%bb%a3%e7%a0%81">#</a>
|
|
|
</h3>
|
|
|
<p>go代码示例:</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">"fmt"</span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span><span style="color:#75715e">//go:noinline
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">add</span>(<span style="color:#a6e22e">a</span>, <span style="color:#a6e22e">b</span> <span style="color:#66d9ef">int</span>) <span style="color:#66d9ef">int</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">a</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">b</span>
|
|
|
</span></span><span style="display:flex;"><span>}
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">c</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">add</span>(<span style="color:#ae81ff">3</span>, <span style="color:#ae81ff">5</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">c</span>)
|
|
|
</span></span><span style="display:flex;"><span>}
|
|
|
</span></span></code></pre></div><h4 id="go-tool-compile">
|
|
|
go tool compile
|
|
|
<a class="anchor" href="#go-tool-compile">#</a>
|
|
|
</h4>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go tool compile -N -l -S main.go
|
|
|
</span></span><span style="display:flex;"><span>GOOS<span style="color:#f92672">=</span>linux GOARCH<span style="color:#f92672">=</span>amd64 go tool compile -N -l -S main.go <span style="color:#75715e"># 指定系统和架构</span>
|
|
|
</span></span></code></pre></div><ul>
|
|
|
<li>-N选项指示禁止优化</li>
|
|
|
<li>-l选项指示禁止内联</li>
|
|
|
<li>-S选项指示打印出汇编代码</li>
|
|
|
</ul>
|
|
|
<p>若要禁止指定函数内联优化,也可以在函数定义处加上<code>noinline</code>编译指示:</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">//go:noinline
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">add</span>(<span style="color:#a6e22e">a</span>, <span style="color:#a6e22e">b</span> <span style="color:#66d9ef">int</span>) <span style="color:#66d9ef">int</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">a</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">b</span>
|
|
|
</span></span><span style="display:flex;"><span>}
|
|
|
</span></span></code></pre></div><h4 id="go-tool-objdump">
|
|
|
go tool objdump
|
|
|
<a class="anchor" href="#go-tool-objdump">#</a>
|
|
|
</h4>
|
|
|
<p>方法1: 根据目标文件反编译出汇编代码</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go tool compile -N -l main.go <span style="color:#75715e"># 生成main.o</span>
|
|
|
</span></span><span style="display:flex;"><span>go tool objdump main.o
|
|
|
</span></span><span style="display:flex;"><span>go tool objdump -s <span style="color:#e6db74">"main.(main|add)"</span> ./test <span style="color:#75715e"># objdump支持搜索特定字符串</span>
|
|
|
</span></span></code></pre></div><p>方法2: 根据可执行文件反编译出汇编代码</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go build -gcflags<span style="color:#f92672">=</span><span style="color:#e6db74">"-N -l"</span> main.go -o test
|
|
|
</span></span><span style="display:flex;"><span>go tool objdump main.o
|
|
|
</span></span></code></pre></div><h4 id="go-build--gcflags--s">
|
|
|
go build -gcflags -S
|
|
|
<a class="anchor" href="#go-build--gcflags--s">#</a>
|
|
|
</h4>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go build -gcflags<span style="color:#f92672">=</span><span style="color:#e6db74">"-N -l -S"</span> main.go
|
|
|
</span></span></code></pre></div><h4 id="其他方法">
|
|
|
其他方法
|
|
|
<a class="anchor" href="#%e5%85%b6%e4%bb%96%e6%96%b9%e6%b3%95">#</a>
|
|
|
</h4>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="https://en.wikipedia.org/wiki/Objdump">objdump命令</a></li>
|
|
|
<li>
|
|
|
<a href="https://github.com/golang/gofrontend">go编译器:gccgo</a></li>
|
|
|
<li>
|
|
|
<a href="https://go.godbolt.org/">在线转换汇编代码:godbolt</a></li>
|
|
|
</ul>
|
|
|
<h2 id="进一步阅读">
|
|
|
进一步阅读
|
|
|
<a class="anchor" href="#%e8%bf%9b%e4%b8%80%e6%ad%a5%e9%98%85%e8%af%bb">#</a>
|
|
|
</h2>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="https://golang.org/doc/asm">Go官方:A Quick Guide to Go’s Assembler</a></li>
|
|
|
<li>
|
|
|
<a href="https://github.com/cch123/golang-notes/blob/master/assembly.md">plan9 assembly 完全解析</a></li>
|
|
|
<li>
|
|
|
<a href="https://en.wikibooks.org/wiki/X86_Assembly#Table_of_Contents">x86 Assembly book</a></li>
|
|
|
<li>
|
|
|
<a href="https://www.section.io/engineering-education/what-is-an-abi/">What is an ABI?</a></li>
|
|
|
</ul>
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<footer class="book-footer">
|
|
|
|
|
|
<div class="flex flex-wrap justify-between">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){if(window.getSelection().toString())return;e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="book-comments">
|
|
|
|
|
|
<script src="https://giscus.app/client.js" data-repo="cyub/go-1.14.13"
|
|
|
data-repo-id="MDEwOlJlcG9zaXRvcnkzMzc2ODUyMzQ=" data-category="Announcements"
|
|
|
data-category-id="DIC_kwDOFCCq8s4CZ3BC"
|
|
|
data-mapping="pathname" data-strict="0" data-emit-metadata="0"
|
|
|
data-input-position="bottom" data-reactions-enabled="0"
|
|
|
data-lang="zh-CN" data-theme="preferred_color_scheme" crossorigin="anonymous" async>
|
|
|
</script>
|
|
|
<noscript>Please enable JavaScript to view the comments powered by giscus.</noscript>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<label for="menu-control" class="hidden book-menu-overlay"></label>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
</body>
|
|
|
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|