NASM - The Netwide Assembler
NASM Forum => Using NASM => Topic started by: Ra.M. on April 16, 2023, 05:29:00 PM
-
Regards.
Some hundred years ago I wrote a little assembly program for testing various types of jumps in DOS real mode (emulated by DOSBox); this is a simplified version of the source code JMP.ASM
; nasm -f obj jmp.asm
; link jmp.obj
CPU 386 ; 32 bit instructions set
%assign STACK_SIZE 0400h ; 1024 bytes for the stack
;############### code segment 1 ##################
SEGMENT CODESEGM1 ALIGN=16 PUBLIC USE16 CLASS=CODE
..start: ; entry point
; JMP direct inter-segment (Ptr16:Ptr16)
jmp far dest_label
return_label:
mov ah, 4ch ; Terminate Program
mov al, 00h ; exit code = 0
int 21h ; DOS INT
;############### code segment 2 #################
SEGMENT CODESEGM2 ALIGN=16 PUBLIC USE16 CLASS=CODE
dest_label:
; JMP direct inter-segment (Ptr16:Ptr16)
jmp far return_label
;################# stack segment ##################
SEGMENT STACKSEGM ALIGN=16 STACK USE16 CLASS=STACK
resb STACK_SIZE ; 1024 bytes for the stack
As you can see, there are two code segments. In CODESEGM1 there is a far jump (direct inter-segment) to CODESEGM2 and in CODESEGM2 there is a far jump (direct inter-segment) to CODESEGM1.
Assembling JMP.ASM with an old NASM version (2.11) and linking it with whatever linker for DOS (for example, LINK.EXE provided by MASM 6.11), I get an executable JMP.EXE that works properly; conversely, using the latest NASM 2.16.1, I get an executable that crashes!
More precisely (and this is really weird), the first far jump works, but the second far jump freezes DOSBox!
As a workaround, with NASM 2.16.1 I have to write:
jmp CODESEGM1:return_label
In order to investigate this problem, consider these two instructions for the second jump:
jmp CODESEGM1:return_label
jmp far return_label
Assembling with NASM 2.11, linking with LINK.EXE and disassembling with NDISASM, this is the result:
EA0A000000 jmp word 0x0:0xa
EA0A000000 jmp word 0x0:0xa
As you can see, both instructions produce the same opcodes; using NASM 2.16.1, instead, this is the result:
EA0A000000 jmp word 0x0:0xa
EA0A000A00 jmp word 0xa:0xa
The address in the second instruction is clearly wrong!
It seems something like JMP Offset:Offset; in fact, if I move return_label to another offset (for example, 0x3c), I get:
EA3C000000 jmp word 0x0:0x3c
EA3C003C00 jmp word 0x3c:0x3c
Again, JMP Offset:Offset!
If I am not wrong, this is a serious bug introduced by NASM 2.16.1.
In similar circumstances, this also happens with the "CALL far" instruction.
-
Aren't you forgetting about relocations?
C:\work> nasm -fobj test.asm -o test.obj
C:\work> tlink test.obj, test.exe
C:\work> ndisasm -b 16-e 512 test.exe
00000000 EA00000100 jmp 0x1:0x0
00000005 B8004C mov ax,0x4c00
00000008 CD21 int 0x21
0000000A 0000 add [bx+si],al
0000000C 0000 add [bx+si],al
0000000E 0000 add [bx+si],al
00000010 EA05000000 jmp 0x0:0x5
Also:
C:\Work> tdump test.exe
...
Relocations:
0000:0003 0001:0003
PS: 'mov ah,4Ch/mov al,0' is the same as 'mov ax,4C00h'.
I know the "bug" is not about that, but I couldn't reproduce it (since I'm still using nasm 2.15).
-
I know the "bug" is not about that, but I couldn't reproduce it (since I'm still using nasm 2.15).
Yes, all NASM versions up to 2.15.x work properly; this problem has appeared in NASM since version 2.16.x.
Probably, my explanation was not clear; in order to compare the two far addresses CODESEGM1:return_label and far return_label, I have put together the two instructions:
jmp CODESEGM1:return_label
jmp far return_label
NASM <= 2.15.x generates these two identical opcodes:
EA0A000000 jmp word 0x0:0xa
EA0A000000 jmp word 0x0:0xa
(0x0 is the Seg component and 0xa is the Offset component of the far address, before relocation)
NASM 2.16.x, instead, generates these two different opcodes:
EA0A000000 jmp word 0x0:0xa
EA0A000A00 jmp word 0xa:0xa
As you can see, in the second instruction the Seg component of the far address is wrong!
-
Probably, my explanation was not clear; in order to compare the two far addresses CODESEGM1:return_label and far return_label, I have put together the two instructions:
jmp CODESEGM1:return_label
jmp far return_label
NASM <= 2.15.x generates these two identical opcodes:
EA0A000000 jmp word 0x0:0xa
EA0A000000 jmp word 0x0:0xa
(0x0 is the Seg component and 0xa is the Offset component of the far address, before relocation)
NASM 2.16.x, instead, generates these two different opcodes:
EA0A000000 jmp word 0x0:0xa
EA0A000A00 jmp word 0xa:0xa
As you can see, in the second instruction the Seg component of the far address is wrong!
Yes, but there is something strange here. Since your segments are aligned by DQWORD (16 bytes), the initial offsets of both CODESEGM1 and CODESEGM2 should be 0x???0. There's no sense the first far jump jumping to 0xXXXX:0xXXXA (and the second should jump to 0xXXXX:0xXXX5). Unless that is not the entire code to test...
-
Yes, but there is something strange here. Since your segments are aligned by DQWORD (16 bytes), the initial offsets of both CODESEGM1 and CODESEGM2 should be 0x???0. There's no sense the first far jump jumping to 0xXXXX:0xXXXA (and the second should jump to 0xXXXX:0xXXX5). Unless that is not the entire code to test...
Yes, you are right; for simplicity, I have removed some instructions.
return_label offset is 0x5 (five bytes after jmp far dest_label), but it doesn't change anything; NASM 2.16.x generates a broken executable.
You can try this code with NASM 2.15.x and NASM 2.16.x
; nasm -f obj jmp.asm
; link jmp.obj
CPU 386 ; 32 bit instructions set
%assign STACK_SIZE 0400h ; 1024 bytes for the stack
;############### code segment 1 ##################
SEGMENT CODESEGM1 ALIGN=16 PUBLIC USE16 CLASS=CODE
..start: ; entry point
; JMP direct inter-segment (Ptr16:Ptr16)
jmp far dest_label
return_label:
mov ah, 4ch ; Terminate Program
mov al, 00h ; exit code = 0
int 21h ; DOS INT
;############### code segment 2 #################
SEGMENT CODESEGM2 ALIGN=16 PUBLIC USE16 CLASS=CODE
dest_label:
; JMP direct inter-segment (Ptr16:Ptr16)
jmp far return_label
;################# stack segment ##################
SEGMENT STACKSEGM ALIGN=16 STACK USE16 CLASS=STACK
resb STACK_SIZE ; 1024 bytes for the stack
-
Yes, you are right; for simplicity, I have removed some instructions.
...
You can try this code with NASM 2.15.x and NASM 2.16.x
Well... I've tried and you are right, with msys2:
$ nasm -fobj -o test.obj test.asm
In DosBOX:
c:\work> tlink test.obj, test.exe
c:\work> tdump test.exe
...
Relocations
0000:0003 0001:0003
Back on MSYS2:
$ ndisasm -b 16 -e 512 test.exe
00000000 EA00000100 jmp 0x1:0x0
00000005 B8004C mov ax,0x4c00
00000008 CD21 int 0x21
0000000A 0000 add [bx+si],al
0000000C 0000 add [bx+si],al
0000000E 0000 add [bx+si],al
00000010 EA05000500 jmp 0x5:0x5
Yep... segment portion of second jmp far is wrong.
Anyway, I do prefer to specify segment:offset in far jumps (calls) instead leting the compiler figure it out. But, of course, I think it should!
-
Anyway, I do prefer to specify segment:offset in far jumps (calls) instead leting the compiler figure it out. But, of course, I think it should!
In any case, this is a really serious bug. I think it would be better to file a bug report to bugzilla/nasm.
Do I need a different account to access bugzilla?
-
In any case, this is a really serious bug. I think it would be better to file a bug report to bugzilla/nasm.
Do I need a different account to access bugzilla?
I agree... I'm not a member of NASM development team (I believe Frank Kotler is), but the bugzilla URL is this one: https://bugzilla.nasm.us/ (https://bugzilla.nasm.us/). Don't know if requires a different login...
-
am old and tired and my memory is all shot. I am not in a position to test 16 bit code. Should the instruction not be:
jmp far [dest_label]
?
Best,
Frank
-
Should the instruction not be:
jmp far [dest_label]
?
That is a far jump indirect inter-segment; in that case, dest_label is a 32 bit variable containing a pair Seg:Offset.
For a far jump direct inter-segment, the syntax is:
jmp far dest_label
In 16 bit real mode this means: "jump to the dest_label offset calculated with respect to the dest_label segment".
That instruction works properly with all NASM versions up to 2.15.x.
NASM 2.16.x, instead, generates an incorrect opcode; something like:
jmp Offset:Offset
As you can see, NASM 2.16.x puts Offset instead of Seg.
I'm not sure but, this bug could also affect addresses in 32/64 bit protected mode.
-
Okay, I stand corrected.
Best,
Frank
-
I'm not sure but, this bug could also affect addresses in 32/64 bit protected mode.
Probably not, because in 32/64 codes, usually, the memory model is FLAT. No need to do a jump intersegment.
-
As you can see, both instructions produce the same opcodes; using NASM 2.16.1, instead, this is the result:
Code: [Select]
EA0A000000 jmp word 0x0:0xa
EA0A000A00 jmp word 0xa:0xa
The address in the second instruction is clearly wrong!
Guess when I first looked at it hundreds of years ago it looked like both of the instructions had distinct opcodes because they were in fact not the same haha (joke) :)
-
As you can see, both instructions produce the same opcodes; using NASM 2.16.1, instead, this is the result:
Code: [Select]
EA0A000000 jmp word 0x0:0xa
EA0A000A00 jmp word 0xa:0xa
The address in the second instruction is clearly wrong!
Does this bug still exist in the latest RC (release candidate) version?
Guess when I first looked at it hundreds of years ago it looked like both of the instructions had distinct opcodes because they were in fact not the same haha (joke) :)