win11下nasm编写汇编及链接方案

一、安装nasm并编写代码

安装nasm添加PATH环境变量

在终端输入nasm -v看到版本即为安装成功

依旧从简单的输出开始,编写如下代码:

asm 复制代码
      global  main  //定义全局符号
        extern  puts  //外部函数
        section .text  //text节区
main:  //符号main的开始,之后程序链接时指定从这开始,OEP将会指向这
        sub     rsp, 28h                        ; 影子空间
        mov     rcx, message                    ; rcx传第一个参数
        call    puts                            ; 调用puts
        add     rsp, 28h                        ; 删除影子空间
        ret
message:  ;变量符号
        db      'Hello', 0                      ;\0结尾的字符串

对于一个可执行文件生成过程:

高级语言-->c语言-->预处理define等宏-->编译为汇编代码-->汇编为obj文件-->链接为可执行文件如exe等

使用命令进行汇编,其中O1表示优化,为可选参数

asm 复制代码
nasm -f win64 hello.asm -o hello.obj         -O1

此时生成的是.obj文件


二、使用gcc.exe或link.exe链接

1.使用gcc链接为例

可以使用电脑上的gcc.exe链接或link.exe链接(在安装一些软件时会附带安装一些gcc,如编译工具和编辑器等)。但是链接后都太大

这里以gcc为例

  • mconsole
    指定 Windows 子系统为控制台程序。这个选项告诉链接器生成一个控制台应用程序(入口函数为 main 或 wmain,启动时会自动分配一个控制台窗口)。与之相对的是 -mwindows(GUI 程序,没有控制台窗口)。
asm 复制代码
gcc hello.obj -o hello.exe -mconsole -lmsvcrt -v
  • lmsvcrt
    链接时去查找并链接 msvcrt 库。具体来说:
    -l 选项表示链接一个库,库文件的实际名称在 Unix 风格下是 lib.a 或 .lib。

msvcrt 对应 Microsoft Visual C++ 运行时库的导入库(例如 MinGW 提供的 libmsvcrt.a),该库用于动态链接到系统自带的 msvcrt.dll(Windows 上最基础的 C 运行时)。

加上这个选项后,程序运行时会使用 MSVC 的 C 运行时(如 printf、malloc 等函数的实现),这对于由 MSVC 编译出的 .obj 文件来说是自然且必要的,否则可能出现符号未定义或运行时冲突。

不显式指定 -lmsvcrt,大多数情况下 GCC(MinGW)仍会自动链接,通常不会出错。但这取决于你具体的 MinGW 发行版和配置

可以使用-v参数查看详细信息,这里我用的是strawberry_per的gcc

大至约64kB,很大一部分原因是因为静态链接了C函数库

查看发现主要是许多节区大小占据大量空间,一方面节区数量多,另一方面文件对齐导致PE文件臃肿

2.链接后大体积原因

为什么会这样?

  1. 链接器默认引入了一大堆库(包括静态库)

    即便有"动态链接 C 库"(-lmsvcrt),这些库里有几个是默认静态链接的:
    libgcc.a、libgcc_eh.a ------ GCC 的低级运行时(除法和模运算、异常展开、栈回溯等)
  • libmingw32.a: MinGW 提供的启动辅助代码
  • libmingwex.a : MinGW 扩展数学函数等
  • libmoldname.a : 一些符号别名
    这些静态库的体积会被直接嵌入你的 hello.exe
  1. 工具链实际上是 UCRT 版本,却链接了 msvcrt

    这是一个 UCRT 版本的 MinGW(名字里有 ucrt),默认运行时应该是 UCRT(libucrt.a),但链接命令里硬编码的是 -lmsvcrt。
    可能导致两种 CRT 的支持代码可能都被部分引入,增大体积
  2. 调试信息------最大的隐形杀手
    hello.obj 由 MSVC 生成,很有可能带有调试信息节(.debugS、.debugS、.debugS、.debugT)。GCC 链接时没有加 -s 或 -Wl,--strip-all,这些调试节会被完整拷贝到 hello.exe 里,让体积成倍增长
  3. 没有优化体积和去除无用节
    链接命令里没有:
  • -Os(优化体积)
  • -s(去除符号表)
  • -Wl,--gc-sections(垃圾收集未用到的节)
  • -fno-ident(去掉 GCC 版本注释节)
  • -fno-exceptions(如果是 C++,去异常表)
体积来源 大约贡献 可优化?
静态链接的 libgcc / libmingwex 20~100 KB 可以强制动态链接 libgcc(-shared-libgcc),但需附带 DLL
CRT 启动文件 (crt2.o 等) 3~10 KB 不可避免
MSVC 的 .obj 调试信息 可能 >100 KB 用 strip hello.exe 或编译时 gcc -s 去除
重复的库 / 多余符号 1~5 KB 加 -Wl,--gc-sections 配合 -ffunction-sections 编译源文件(对 .obj 无效,因为是预编译的)
PE 对齐与元数据 1~2 KB 基本不可减

3.去除符号表后的效果

加上-s去除符号表后仅剩余22kB

以下为符号表去除前后的区别

三、使用golink链接

GoLink是一款专注于提高开发效率和系统可维护性的链接管理工具

为实现极小的大小,这里下载一个golink.exe ,同样添加环境变量

bash 复制代码
golink /entry:main /console kernel32.dll msvcrt.dll hello.obj

其中/entry指定入口点,/console指定控制台应用,之后两dll指定不要静态链接kernel32.dll和msvcrt.dll( Microsoft Visual C++ 运行时库)

此时即可运行程序,仅1.5kB,连文件中的一页4k都不到

调试到入口点,可以看到仅几条机器指令

此时可以看到只有一个start符号的函数

节区数据

最后效果

相关推荐
ThornArmor19 天前
【工具篇·番外】跨语言生态的主权回收:基于 ISA 说明书的 4-bit 双向汇编系统全线封顶
c语言·开发语言·汇编·c++·重构·架构
是星辰吖~19 天前
WIN32_线程(下)
汇编
是星辰吖~19 天前
WIN32_线程(上)
汇编
William.csj20 天前
Linux——普通用户离线源码编译 gcc-9 方法和调用教程
linux·服务器·gcc
AI科技星20 天前
数术工坊 · 第四卷 橡皮泥江湖(拓扑学)【完整定稿】
c语言·开发语言·汇编·electron·概率论·拓扑学
iCxhust21 天前
C# 生成命令行程序 将hex格式烧录程序转换成bin烧录格式
开发语言·汇编·单片机·嵌入式硬件·c#·微机原理
iCxhust21 天前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
hhcgchpspk21 天前
汇编语言传递数据和地址的误区
汇编·笔记·nasm·masm
iCxhust21 天前
MTK8088单板机制作(一)时钟电路
汇编·单片机·嵌入式硬件·微机原理·8088单板机
iCxhust21 天前
8086 汇编位测试使用方法
汇编·单片机·嵌入式硬件·微机原理·8088单板机