鱼C论坛

 找回密码
 立即注册
查看: 1004|回复: 7

[已解决]关于nasm汇编和masm汇编分段问题

[复制链接]
发表于 2023-12-5 17:56:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 soul3500 于 2023-12-5 17:58 编辑

在学习x86汇编中,发现很多书籍用的都是nasm,在学习后,对比masm发现两者分段有很多弄不懂的地方,求助一下汇编的大佬们;

研究masm和nasm汇编 对于段地址以及偏移地址的问题:

masm:
        datasg1 segment
            db 2
        datasg1 ends

        assume cs:codesg0
        codesg0 segment word
              start: mov ax,10
                    mov ax,offset start
                     mov ax,offset start2
                     mov ax,datasg1
                     mov ax,codesg0
        codesg0 ends

        assume cs:codesg1
        codesg1 segment word
                 start2: mov ax,10
                    mov ax,codesg1
        codesg1 ends
        end start

在masm中定义了一个数据段,以及2个代码段;其中代码段对齐方式为字节对齐
在进行编译和链接后;由于字节对齐,而不是16位对齐
因此codesg和datasg出现了段重叠,8086中codesg的段起始地址为datasg的段起始地址,并且start的偏移地址以datasg段为起始向下计算字节,也就是0002。
       

nasm:
        section data1 align=2
           db 2

        section code1 align=2 vstart=0
        start:  mov ax,10
                mov ax,start
                mov ax,section.code1.start  
        (section 为定义段,align 对应对齐方式,vstart为段内偏移)

在nasm中定义了一个数据段 data1,以及一个代码段code1;同样是字节对齐
在进行编译生成bin二进制文件后,发现没有出现段重叠现象,并且section.code1.start 段起始地址为0002(逻辑),也就是定义db 2后,开始的地址;并且code1段中的start起始偏移地址还是为0



以下是编译后的二进制
masm:
        02 00 B8 0A 00 B8 02 00 B8 02 00 B8 00 00 B8 00
        00 00 B8 0A 00 B8 01 00
       
        02 00: db 2
        B8 0A 00:mov ax, 10
        B8 02 00:        mov ax, offset start
        B8 02 00:        mov ax, offset start2
        B8 00 00:        mov ax, datasg1
        B8 00 00:        mov ax, codesg0
        00:        字节对齐
        B8 0A 00:mov ax, 10
        B8 01 00:        mov ax, codesg1

nasm:
        02 00 B8 0A 00 B8 00 00 B8 02 00
       
        02 00: db 2
        B8 0A 00: mov ax,10
        B8 00 00: mov ax,start
        B8 02 00: mov ax,section.code1.start

       
        因此我想请教一下为什么nasm中没有出现段重叠,并且start起始地址为何还是0


最佳答案
2024-5-15 21:05:49
本帖最后由 thinklf 于 2024-5-15 21:27 编辑

首先你分析的就不对(个人意见,仅供参考):
1、对于masm部分,你的描述如下:
在masm中定义了一个数据段,以及2个代码段;其中代码段对齐方式为字节对齐;
在进行编译和链接后;由于字节对齐,而不是16位对齐。
因此codesg和datasg出现了段重叠,8086中codesg的段起始地址为datasg的段起始地址,并且start的偏移地址以datasg段为起始向下计算字节,也就是0002。

你的2个数据段都是字对齐(word),并不是字节对齐,字节对齐是byte。
word对齐,则2个字节向后推,即0、2、4依次,因datasg1的前16字节可以容纳至少1个word,则导致datasg1和codesg0段地址相同。你如果把db 2改成db 15 dup(2),你会发现codesg0的段地址+1了;如果将codesg0的对其方式改成byte,则datasg1和codesg0段地址仍然相同。
offset start的值为什么是2,因为codesg0和datasg1段地址相同,且codesg0是word对齐,偏移地址0有数据2,第一个word段0-1这2个字节不能用,第二个word段2-3可以用,因此start的偏移地址是2。
你可以将codesg0的对齐方式改为byte,发现start的偏移地址就是1了。你改成para,则codesg0的段地址+1,start的偏移地址是0(因为段地址不同)。
2、对于nasm部分,你的描述如下:
在nasm中定义了一个数据段 data1,以及一个代码段code1;同样是字节对齐;
在进行编译生成bin二进制文件后,发现没有出现段重叠现象,并且section.code1.start 段起始地址为0002(逻辑),也就是定义db 2后,开始的地址;并且code1段中的start起始偏移地址还是为0

align=2是字对齐,即2个字节,如果字节对齐是align=1啊。
这里要声明一点,nasm中的section和masm中的segment并不是完全对应关系,masm中segment的本质就是段地址(段地址可以通过segment*16+offset得到20位真实地址),nasm中section的地址却只是从程序开始到section定义处的偏移地址,section不能通过section*16+offset得到属于该section中的数据地址。
因此,section.code1.start的地址就是整个程序的偏移地址,偏移地址0处是:db 2,因align=2,所以offset 1处不行,2可以;将align改成1,section.code1.start的值就是1。
vstart=0的作用就是让section中的标号从section开始计算偏移地址,如果不加vstart,就是从程序开始出计算偏移地址, 这样做只是为了方便定位section中的标号。
并且这里还有一个问题,你加上了vstart=0,start的偏移地址就是0,但code1中的代码就很难访问到这个数据。通过[start]访问,ds:[0]是啥?ds的值是section的地址么?如果手动将section的地址赋值给ds,那么section*16+0计算出来的20位地址,是数据的地址么?
你问nasm中为什么没有出现段重叠,那是因为section地址根本就不是所谓的段,他只是一个符合align设置的偏移地址。
这个问题你可以参考《x86汇编语言  从实模式到保护模式》的第8章。
这样讲,masm要更加智能一些,帮助你规划段。但nasm不管,段的规划需要你手动操作。

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-12-5 17:57:15 | 显示全部楼层
在x86汇编中,段寄存器的作用是指示当前代码或数据所在的段,在内存中,每个段的起始地址都由段寄存器指定。MASM和NASM在处理分段问题上有一些差异。

首先,MASM使用的是分段机制中的默认分段模型(tiny model),即只有一个数据段和一个代码段,段寄存器CS指向代码段,其他段寄存器(如DS、ES、SS)指向数据段。在代码中,使用`assume`语句来定义段寄存器的关系。

而NASM则没有默认分段模型的概念,所有的段都需要显式地定义。你可以使用`section`语句来定义不同的段,在代码中使用标签来引用段中的位置。

根据你提供的MASM代码和NASM代码,我们来分析一下。

对于MASM代码,由于采用了默认分段模型,所以没有显式地定义段寄存器的值,默认情况下`assume cs:codesg0`,即代码段CS指向codesg0,因此start的偏移地址是相对于codesg0段的起始地址。而codesg0段和datasg1段虽然出现了段重叠,但是由于实际执行的时候CS寄存器的值是codesg0,所以访问的是codesg0段的内容。

对于NASM代码,段的定义比较明确,数据段和代码段都是通过`section`语句显式定义的。在代码中,使用标签来引用代码段中的位置,如`section.code1.start`,这个标签的值就是相对于code1段起始地址的偏移量。而由于没有默认分段模型的限制,段之间没有发生重叠。

至于为什么NASM编译生成的二进制文件中start的偏移地址还是0,这是因为NASM在生成二进制文件时会按照段的起始地址进行偏移计算。在你的代码中,db 2定义在data1段的起始地址处,所以start的偏移地址是相对于data1段的起始地址的偏移量,而data1段的起始地址就是0,所以start的偏移地址为0。

希望以上解答能够对你有所帮助,如果还有其他问题,请继续提问!

以上回复来自 -- ChatGPT(FishC官方接口),如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-5-15 21:05:49 | 显示全部楼层    本楼为最佳答案   
本帖最后由 thinklf 于 2024-5-15 21:27 编辑

首先你分析的就不对(个人意见,仅供参考):
1、对于masm部分,你的描述如下:
在masm中定义了一个数据段,以及2个代码段;其中代码段对齐方式为字节对齐;
在进行编译和链接后;由于字节对齐,而不是16位对齐。
因此codesg和datasg出现了段重叠,8086中codesg的段起始地址为datasg的段起始地址,并且start的偏移地址以datasg段为起始向下计算字节,也就是0002。

你的2个数据段都是字对齐(word),并不是字节对齐,字节对齐是byte。
word对齐,则2个字节向后推,即0、2、4依次,因datasg1的前16字节可以容纳至少1个word,则导致datasg1和codesg0段地址相同。你如果把db 2改成db 15 dup(2),你会发现codesg0的段地址+1了;如果将codesg0的对其方式改成byte,则datasg1和codesg0段地址仍然相同。
offset start的值为什么是2,因为codesg0和datasg1段地址相同,且codesg0是word对齐,偏移地址0有数据2,第一个word段0-1这2个字节不能用,第二个word段2-3可以用,因此start的偏移地址是2。
你可以将codesg0的对齐方式改为byte,发现start的偏移地址就是1了。你改成para,则codesg0的段地址+1,start的偏移地址是0(因为段地址不同)。
2、对于nasm部分,你的描述如下:
在nasm中定义了一个数据段 data1,以及一个代码段code1;同样是字节对齐;
在进行编译生成bin二进制文件后,发现没有出现段重叠现象,并且section.code1.start 段起始地址为0002(逻辑),也就是定义db 2后,开始的地址;并且code1段中的start起始偏移地址还是为0

align=2是字对齐,即2个字节,如果字节对齐是align=1啊。
这里要声明一点,nasm中的section和masm中的segment并不是完全对应关系,masm中segment的本质就是段地址(段地址可以通过segment*16+offset得到20位真实地址),nasm中section的地址却只是从程序开始到section定义处的偏移地址,section不能通过section*16+offset得到属于该section中的数据地址。
因此,section.code1.start的地址就是整个程序的偏移地址,偏移地址0处是:db 2,因align=2,所以offset 1处不行,2可以;将align改成1,section.code1.start的值就是1。
vstart=0的作用就是让section中的标号从section开始计算偏移地址,如果不加vstart,就是从程序开始出计算偏移地址, 这样做只是为了方便定位section中的标号。
并且这里还有一个问题,你加上了vstart=0,start的偏移地址就是0,但code1中的代码就很难访问到这个数据。通过[start]访问,ds:[0]是啥?ds的值是section的地址么?如果手动将section的地址赋值给ds,那么section*16+0计算出来的20位地址,是数据的地址么?
你问nasm中为什么没有出现段重叠,那是因为section地址根本就不是所谓的段,他只是一个符合align设置的偏移地址。
这个问题你可以参考《x86汇编语言  从实模式到保护模式》的第8章。
这样讲,masm要更加智能一些,帮助你规划段。但nasm不管,段的规划需要你手动操作。

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-5-16 08:45:25 | 显示全部楼层
本帖最后由 thinklf 于 2024-5-16 08:52 编辑

关于这个问题,我还有一点补充,你通过nasm编译时,一定是nasm -f bin(bin格式是默认的),因为只有bin格式下才支持section.code1.start这样的语法。bin是平坦模式,即操作系统直接将bin文件加载入内存,然后从头开始执行,不像omf,elf格式那样有程序入口点等,如dos的com、sys文件等,还有就是mbr程序,这些文件是典型的不用分段的,比如com就规定只能一个段。
如果是本例,你应该通过nasm -f obj,然后alink生成exe来作对比。
nasm由于支持较多的可执行程序格式,不同格式又支持不同语法。
nasm对于dos中的16位exe的支持是相当完备的,可直接使用segment语法,你就会发现,你所关心的问题其实都是一致的。
详细请参照nasm官方文档第8章《Chapter 8: Output Formats》,其中关于dos下16位exe的文件格式obj(dos omf),《8.4 obj: Microsoft OMF Object Files》,你就完全明白了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-5-28 16:16:23 | 显示全部楼层
本帖最后由 soul3500 于 2024-5-28 16:57 编辑
thinklf 发表于 2024-5-16 08:45
关于这个问题,我还有一点补充,你通过nasm编译时,一定是nasm -f bin(bin格式是默认的),因为只有bin格 ...


谢谢大佬的指正。
我主要的困惑点主要是nasm和masm他的分段机制有点模糊不清:
     masm汇编,感觉像是根据代码分段,编译器自动给你按照每一个代码段,数据段分配好放入内存。然后里面的伪指令 或者偏移地址也是按照每个段每个段的去偏移。如果出现重叠,对应的偏移地址也会自动的划分。
而nasm没有清楚它具体是怎么去划分段的。因为感觉偏移地址一下是按照段偏移,一下又是按照程序的起始地址偏移的,就很混乱。

就是说 nasm是按照x86的平坦模式来做的,就是整个程序处于一个大段,代码中的section 其实都只是便于编写结构化程序时才说明是所谓的段。其实都是编译地址?然后vstart作用就是将这个section下的指令地址"原来以程序开头位置偏移"修改成"以section+vstart开始进行偏移“是吗?
然后,代码段,数据段,堆栈段都是0开始?

而masm 是按照segment 直接映射物理划分出每个段?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-5-30 16:09:53 | 显示全部楼层
thinklf 发表于 2024-5-16 08:45
关于这个问题,我还有一点补充,你通过nasm编译时,一定是nasm -f bin(bin格式是默认的),因为只有bin格 ...

大佬,你好
   就是我用你说的方式 通过"nasm -f obj filename.asm"将代码编译并且用alink链接生成exe文件,貌似obj格式不支持section.code1.start 这类获取地址的语法。因为我想通过获取段地址,来确定对于分配段的的一些困惑。

然后 我编译后align=4 已经不起作用了。查看二进制文件看,即使我设置了align=4 exe中还是按照16字节对齐的。并且我修改了vstart,使得vstart=4; 在mov ax,start 这条指令中中 我看到 start 还是00 00.


这是被编译的代码:
section data1 align=4
   db 4

section code1 align=4 vstart=5
start:  mov ax,10
          mov ax,start

编译连接后的exe文件:
04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
B8 0A 00 B8 00 00
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-6-13 22:53:36 | 显示全部楼层
本帖最后由 thinklf 于 2024-6-13 23:39 编辑
soul3500 发表于 2024-5-28 16:16
谢谢大佬的指正。
我主要的困惑点主要是nasm和masm他的分段机制有点模糊不清:
     masm汇编,感觉 ...


你理解的没错,在平坦模式(-f bin)下,section也只是表示偏移地址,如果不加vstart=0,那么标号的地址也是偏移地址,标号和secttion的地址之间没有任何联系;如果加上vstart=0,那么标号的地址就是距section开始处的距离,其偏移地址就是section+距离,不是section*16+距离啊。平坦模式下没有分段,默认cs、ds、ss都相同。当然你也可以手动分段,就是手动修改cs、ds和ss等段寄存器的值。偏移地址怎么办,手动算啊。所以平坦模式就是用来写com、mbr或驱动程序的。
在masm中,segmen就真实对应到物理段地址啊。
其实这样没有可比性,nasm -f obj模式下,section跟masm的segment的行为相同。只是masm5没有平坦模式,masm6就直接支持了。
还有啊,在nasm中section和segment的意思一样,可以互相替换。在nasm中,不同的-f 模式,相同的代码,行为模式并不一样。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-6-13 23:00:10 | 显示全部楼层
本帖最后由 thinklf 于 2024-6-14 05:05 编辑
soul3500 发表于 2024-5-30 16:09
大佬,你好
   就是我用你说的方式 通过"nasm -f obj filename.asm"将代码编译并且用alink链接生成exe文 ...


nasm在-f obj模式下,segment可以直接通过段名称访问段地址啊(或seg 标号),而不是section.code1.start 。bin模式下,不能通过段名称访问段地址。我估计nasm是为了区分故意这样规定的。
你说align=4不起作用,这就是nasm和masm在段分配上的不同了。masm认为,只要上一个段地址的空间足够,两个段共享空间也可以。但nasm则不同,2个段不共享段地址。一定要从下一个段地址开始。你试试align=256,发现段又往后移动了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-22 02:56

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表