soul3500 发表于 2023-12-5 17:56:29

关于nasm汇编和masm汇编分段问题

本帖最后由 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


FishC_GPT 发表于 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官方接口),如未能正确解答您的问题,请继续追问。

thinklf 发表于 4 天前

本帖最后由 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中的代码就很难访问到这个数据。通过访问,ds:是啥?ds的值是section的地址么?如果手动将section的地址赋值给ds,那么section*16+0计算出来的20位地址,是数据的地址么?
你问nasm中为什么没有出现段重叠,那是因为section地址根本就不是所谓的段,他只是一个符合align设置的偏移地址。
这个问题你可以参考《x86汇编语言从实模式到保护模式》的第8章。
这样讲,masm要更加智能一些,帮助你规划段。但nasm不管,段的规划需要你手动操作。

thinklf 发表于 3 天前

本帖最后由 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》,你就完全明白了。
页: [1]
查看完整版本: 关于nasm汇编和masm汇编分段问题