By chance i came across this problem. I have an app, which tests error recovery features. Linking the . obj file with MS´s linker just works fine, using polink the resulting .exe crashes. Investigating this further i found that polink resolves the local label "__el1" incorrect as it seems (assembler snippet "error.asm" line 18). This label name ("__el1") occurs in more than one procedure, but this shouldn´t be a problem, because it is a local label. Polink makes the corresponding jump go into a different procedure to a label with the same name, which leads to GPF further on. The attached .obj file has got inserted an "int 3" before the offending "jmp" in order to be able to easily find the error location with a debugger. Please study the supplied files in the attachment.
Thanks
JK
Hi jk,
Could you please provide the complete source code so we can reproduce the issue?
The original source is assembler and i use a very special setup, which at the moment is WIP and therefore scattered all over my HD - could be a major task reproducing.
That is why i supplied the assembled .obj file. As said avove the error occurs when linking with polink, it doesn´t occur with MS´s linker from Visual Studio. MS´s stuff is overbloated for what i need, therefore i tried polink. It works perfectly well in all of my other apps, but it fails here (al least i suppose it´s a linker problem).
The attachement contains the .obj file, the resulting .exe files for polink.exe and link.exe (MS), the batch commandline for polink (incl. command file), two debugger screenshots and a snippet of the relevant assembler code. As you can see, this isn´t regular assembler code, among other things my setup maps e.g xax to eax and rax respectively, that is, the same code can be assembled in 32 and 64 bit.
As already said above, my current best bet is, that the jump address in line 18 of the snippet isn´t "calculated" correctly in the linking process, because the name of the label "__el1" is not unique. It is unique per procedure, but it is not unique all over the .obj file, because a label with same name is present in several procedures. I hope this is sufficient data to investigate what happens.
Hi jk,
So have you tried to prove it is a multiple label name use issue by changing to unique names?
From what you said there should be no issue using unique names since they are supposed to
be procedure scoped. And this should be a relatively minor edit.
Trying to figure out a potential bug from the output result files is much more difficult than starting
from the sources. Perhaps you can created a demo version with just one or two procedures using
an identical 'local' label that shows the bug resulting in a link error.
John Z
I can confirm, that making the offending label name unique over all procedures makes the problem go away.
To my understanding the error occurs somewhere in the linking process, otherwise the executable linked with MS´s linker wouldn´t run properly. In the attachment of post #1 i supplied the . obj file, which is all you need for linking it yourself, as well as additional info as far as i could. I don´t see the advantage of being able to create the .obj file yourself, when you already have this .obj file. In my opinion only the developer(s) of polink can tell, what is wrong here.
I don´t claim that polink is buggy! But i have an .obj file which (when linked to an executable) works fine with MS´s linker and doesn´t with polink. So at first glance there are two options:
- it could be a bug inside the .obj file, but then it would be crucial to have this file for testing and finding out, what´s wrong with the .obj file
- it could be a bug in polink and then it is equally essential to have exactly this .obj file
MAP-file might be useful for checking things ?
I don't know exactly what is happening here, but I suppose that the result could be related at least to 2 different things.
The first is that the symbol, even if not declared global or extern, isn't considered 'local' by the compiler assembler (sorry, but I was used to C compiler ;D).
The second point could be the recently added "select_any" behavior, introduced in version 11, that for multiply defined symbols simply pick one of them.
This is just a speculation :P
I've not done any Intel CPU assembly programing since the 486 came out ;( but looking to refresh my knowledge and for information I found this:
---
Labels
A label can be placed at the beginning of a statement. During assembly, the label is assigned the current value of the active location counter and serves as an instruction operand. There are two types of lables: symbolic and numeric.
Symbolic Labels
A symbolic label consists of an identifier (or symbol) followed by a colon (:) (ASCII 0x3A). Symbolic labels must be defined only once. Symbolic labels have global scope and appear in the object file's symbol table.
Symbolic labels with identifiers beginning with a period (.) (ASCII 0x2E) are considered to have local scope and are not included in the object file's symbol table.
---
If poasm/polink follows this rule then try putting a . in front of the labels that are to be local in scope. This should be easy to try .. the linker may be looking for this identifier.
John Z
reference link (maybe outdated) https://docs.oracle.com/cd/E26502_01/html/E28388/eqbsx.html
I can confirm, that it doesn´t depend on the version of polink, the problem occurs with V 9 as well as with the latest (just tested).
The term "label" alone might be ambiguos, in this case "label" means a jump target, a location in code, where absolute or conditional jumps go to. In assembler code these "labels" must end with a colon, labels with local (procedure) scope get one single colon (e.g. "label:"), global labels get two consecutive colons (e.g. "global_label::"). A dot as first charcater is not allowed, the first character can be an alphabetic character (a-z, A–Z) or any of these four characters: "@ _ $ ?"
Well I'll throw out one more thought then let it be as I'm certainly no expert - Vortex where are you ?? ;)
Pelles Help File:
"The assembler contains a parser for the IA32 (X86) and AMD64 (X86-64) architecture with a syntax similar to MASM, but there are deliberate differences since POASM is not intended to be an exact clone of MASM."
----
https://fragglet.github.io/dos-help-files/alang.hlp/x_cln__cln_.html
"With the single colon, <label> has global scope within its
module under the default OPTION NOSCOPED. Under OPTION SCOPED,
<label> is LOCAL inside a PROC block and is global elsewhere.
The <label> cannot be declared public.
The double colon acts the same as the single colon except that
<label> has global scope within its module, independent of OPTION
SCOPED, and can be declared PUBLIC."
---
Art of Assembly Language chapter 12
https://www.plantation-productions.com/Webster/www.artofasm.com/DOS/ch12/CH12-1.html#HEADING1-9
"By default, MASM 6.x treats statement labels (those with a colon after them) as local to a procedure.
MASM uses the following default scoping rules:
By default, statement labels appearing in a procedure are local to that procedure.
By default, all procedure names are public.
By default, most other symbols are global.
Note that these rules apply to MASM 6.x only. Other assemblers and earlier versions of MASM follow different rules.
Overriding the default on the first rule above is easy - either use the option noscoped statement or use a double colon to make a label global. You should be aware, though, that you cannot make a local label public using the public or externdef directives. You must make the symbol global (using either technique) before you make it public."
----
Makes me believe poasm follows the first "default OPTION NOSCOPED", additionally the poasm help
file makes no mention of having options for NOSCOPED or SCOPED that I could find.
That's all folks ....
John Z
Ok, i boiled it down to the absolute minimum:
ExitProcess PROTO :DWORD
TESTME PROTO
includelib KERNEL32.LIB
.CODE
TESTME PROC USES EBX ESI EDI
;*************************************************************************************
;
;*************************************************************************************
LOCAL _res:XWORD
xor eax, eax
__el1:
test eax, eax
jne L2
lea ebx, __el1
mov _res, ebx
jmp L1 ;jmp to error handler
; some other code
L1:
add eax, 1
JMP _res
L2:
RET
TESTME ENDP
start PROC USES EBX ESI EDI
;*************************************************************************************
;
;*************************************************************************************
LOCAL _res:XWORD
invoke TESTME
int 3
xor edx, edx
__el1:
test edx, edx
jne L2
lea ebx, __el1
mov _res, ebx
jmp L1 ;jmp to error handler
; some other code
L1:
add edx, 1
JMP _res
L2:
INVOKE ExitProcess, edx
start ENDP
END start
Regardless which assembler i use (ml.exe, asmc.exe, uasm.exe) linking with MS´s link.exe works, linking with polink leads to error (see attachments)
Seems the attached images got lost
Great -
Seems you did not try poasm? I don't see it mentioned.
If poasm only uses default NOSCOPED then polink which
is written for poasm would not be expecting otherwise so
linking with polink but using other asm programs would be
an incompatibility. The three assemblers below I expect are
highly compatible with MASM 6.1
John Z
Hi Jk,
A question :
TESTME PROC USES EBX ESI EDI
;*************************************************************************************
;
;*************************************************************************************
LOCAL _res:XWORD
xor eax, eax
__el1:
test eax, eax
jne L2
lea ebx, __el1
mov _res, ebx
jmp L1 ;jmp to error handler
; some other code
L1:
add eax, 1
JMP _res
What is XWORD?
Hi Jk,
After replacing the XWORDs with DWORDs, I managed to assemble your source code with Poasm. Disassembling the object module with Agner Fog's objconv :
.386
option dotname
.model flat
public _TESTME@0
public _start@0
extern _ExitProcess@4: near
@feat.00 equ 00000001H
.drectve SEGMENT BYTE PUBLIC 'CONST'
db 2FH, 64H, 65H, 66H, 61H, 75H, 6CH, 74H
db 6CH, 69H, 62H, 3AH, 4BH, 45H, 52H, 4EH
db 45H, 4CH, 33H, 32H, 2EH, 4CH, 49H, 42H
db 20H, 2FH, 65H, 6EH, 74H, 72H, 79H, 3AH
db 73H, 74H, 61H, 72H, 74H, 40H, 30H, 20H
.drectve ENDS
_text SEGMENT PARA PUBLIC 'CODE'
_TESTME@0 PROC NEAR
push ebp
mov ebp, esp
sub esp, 4
push ebx
push esi
push edi
xor eax, eax
TESTME.__el1 LABEL NEAR
test eax, eax
jnz ?_002
lea ebx, [TESTME.__el1]
mov dword ptr [ebp-4H], ebx
jmp ?_001
?_001: add eax, 1
jmp dword ptr [ebp-4H]
_TESTME@0 ENDP
?_002 LABEL NEAR
pop edi
pop esi
pop ebx
leave
ret
_start@0 PROC NEAR
push ebp
mov ebp, esp
sub esp, 4
push ebx
push esi
push edi
call _TESTME@0
int 3
xor edx, edx
start.__el1 LABEL NEAR
test edx, edx
jnz ?_004
lea ebx, [start.__el1]
mov dword ptr [ebp-4H], ebx
jmp ?_003
?_003: add edx, 1
jmp dword ptr [ebp-4H]
_start@0 ENDP
?_004 LABEL NEAR
push edx
call _ExitProcess@4
pop edi
pop esi
pop ebx
leave
ret
_text ENDS
END
Could you please show the issue(s) here in this output?
Listing the symbols of the object module :
E:\PellesC\Bin>podump.exe /SYMBOLS test.obj
Dump of erol.obj
File type: OBJ
SYMBOL TABLE
0000 00000001 ABS notype static | @feat.00
0001 00000000 SECT1 notype static | .drectve
length of section 28, #relocations 0, #linenumbers 0
0003 00000000 SECT2 notype static | .text
length of section 57, #relocations 3, #linenumbers 0
0005 00000000 UNDEF notype external | _ExitProcess@4
0006 00000000 SECT2 notype () external | _TESTME@0
0007 00000025 SECT2 notype () external | _start@0
0008 0000000B SECT2 notype static | TESTME.__el1
0009 00000037 SECT2 notype static | start.__el1
SUMMARY
28 .drectve
57 .text
Removing the instruction int 3, I rebuilt the application. A session of Ollydbg didn't report any issues.
My bad, it should have been "DWORD" (my setup maps XWORD to DWORD/QWORD in 32/64 bit respectively)
This is what i get with podump:
Dump of Test.obj
File type: OBJ
SYMBOL TABLE
0000 00000000 DEBUG notype filename | .file
C:\Users\Juergen\Desktop\Pelle\Test.tmp
0004 00000000 SECT1 notype static | .text
length of section 53, #relocations 3, #linenumbers 0
0006 00000000 SECT2 notype static | .data
length of section 0, #relocations 0, #linenumbers 0
0008 00000000 SECT3 notype static | .drectve
length of section 28, #relocations 0, #linenumbers 0
000A 00000000 UNDEF notype () external | _ExitProcess@4
000B 00000000 SECT1 notype () external | _TESTME@0
000C 00000027 SECT1 notype () external | _start@0
000D 0000000B SECT1 notype label | ___el1
000E 00000038 SECT1 notype label | ___el1
The offending line in your disassembly is:
lea ebx, [start.__el1]
which is correct, so no problem here
Assembling it with the other assemblers and linking it with polink results in this (as if the code in start proc would have been like so):
lea ebx, [TESTME.__el1]
Obviously poasm creates symbols in a different way and polink expects such symbols ...
As you can see in my dump, the symbol "___el1" is listed twice and there isn´t a prefix indicating the corresponding procedure, which, as it seems, polink needs to get things straight, while MS´s linker can handle it nevertheless.
So definitely not a bug, because polink is tailored for po... things, but because of these special things polink cannot be used for linking everything.
Maybe i´m still overlooking something... A pity - polink would have been a lightweight alternative to MS´s bloated stuff. Anyway, thanks for your help and contributions!
Quote from: jk on August 26, 2023, 05:20:26 PM
So definitely not a bug, because polink is tailored for po... things, but because of these special things polink cannot be used for linking everything.
Maybe i´m still overlooking something... A pity - polink would have been a lightweight alternative to MS´s bloated stuff. Anyway, thanks for your help and contributions!
Good to know that the issue is solved. :)
I'll remove it from the bug reports thread to avoid confusion.
Anyway it's always unsafe to expect that MS things are conformant to any standard... ;D
Good work Vortex! Thanks for answering the call :) :) :)
John Z
Hi John,
You are welcome, thanks.
Hi jk,
It would be preferable to give unique and more descriptive names to your labels to avoid conflicts.
Assembling the code with MS Macro Assembler supplied with the Masm32 package :
E:\PellesC\Bin>\masm32\bin\ml.exe /c /coff test.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: test.asm
Trying to link the object module with Polink :
E:\PellesC\Bin>polink.exe /SUBSYSTEM:WINDOWS test.obj \PellesC\lib\Win\kernel32.lib
POLINK: error: Symbol '__el1' is multiply defined: 'test.obj' and 'test.obj'.
The output of MS Macro Assembler :
_TESTME@0 PROC NEAR
push ebp ; 0000 _ 55
mov ebp, esp ; 0001 _ 8B. EC
add esp, -4 ; 0003 _ 83. C4, FC
push ebx ; 0006 _ 53
push esi ; 0007 _ 56
push edi ; 0008 _ 57
xor eax, eax ; 0009 _ 33. C0
__el1 LABEL NEAR
test eax, eax ; 000B _ 85. C0
jnz ?_002 ; 000D _ 75, 11
lea ebx, [__el1] ; 000F _ 8D. 1D, 00000000(d)
mov dword ptr [ebp-4H], ebx ; 0015 _ 89. 5D, FC
jmp ?_001 ; 0018 _ EB, 00
?_001: add eax, 1 ; 001A _ 83. C0, 01
jmp dword ptr [ebp-4H] ; 001D _ FF. 65, FC
_TESTME@0 ENDP
?_002 LABEL NEAR
pop edi ; 0020 _ 5F
pop esi ; 0021 _ 5E
pop ebx ; 0022 _ 5B
leave ; 0023 _ C9
ret ; 0024 _ C3
_start@0 PROC NEAR
push ebp ; 0025 _ 55
mov ebp, esp ; 0026 _ 8B. EC
add esp, -4 ; 0028 _ 83. C4, FC
push ebx ; 002B _ 53
push esi ; 002C _ 56
push edi ; 002D _ 57
call _TESTME@0 ; 002E _ E8, FFFFFFCD
xor edx, edx ; 0033 _ 33. D2
__el1 LABEL NEAR
test edx, edx ; 0035 _ 85. D2
jnz ?_004 ; 0037 _ 75, 11
lea ebx, [__el1] ; 0039 _ 8D. 1D, 00000000(d)
mov dword ptr [ebp-4H], ebx ; 003F _ 89. 5D, FC
jmp ?_003 ; 0042 _ EB, 00
I tried ml (2013) with -Gy option and result crashed..386
.model flat, stdcall
ExitProcess PROTO :DWORD
TESTME PROTO
includelib KERNEL32.LIB
.CODE
TESTME PROC USES EBX ESI EDI
;*************************************************************************************
;
;*************************************************************************************
LOCAL _res:DWORD
xor eax, eax
__el1:
test eax, eax
jne L2
lea ebx, __el1
mov _res, ebx
jmp L1 ;jmp to error handler
; some other code
L1:
add eax, 1
JMP _res
L2:
RET
TESTME ENDP
start PROC USES EBX ESI EDI
;*************************************************************************************
;
;*************************************************************************************
LOCAL _res:DWORD
invoke TESTME
int 3
xor edx, edx
__el1:
test edx, edx
jne L2
lea ebx, __el1
mov _res, ebx
jmp L1 ;jmp to error handler
; some other code
L1:
add edx, 1
JMP _res
L2:
INVOKE ExitProcess, edx
ret
start ENDP
END start
00000000 55 push ebp
00000001 8BEC mov ebp, esp
00000003 83C4FC add esp, -4h
00000006 53 push ebx
00000007 56 push esi
00000008 57 push edi
00000009 33C0 xor eax, eax
0000000B 85C0 test eax, eax
0000000D 7511 jnz L_20
0000000F 8D1D0B104000 lea ebx, [$+40100Bh]
00000015 895DFC mov dword ptr [ebp-4h], ebx
00000018 EB00 jmp L_1A
0000001A 83C001 add eax, 1h
0000001D FF65FC jmp dword ptr [ebp-4h]
00000020 5F pop edi
00000021 5E pop esi
00000022 5B pop ebx
00000023 C9 leave
00000024 C3 ret
00000025 CC int3
00000026 CC int3
00000027 CC int3
00000028 55 push ebp
00000029 8BEC mov ebp, esp
0000002B 83C4FC add esp, -4h
0000002E 53 push ebx
0000002F 56 push esi
00000030 57 push edi
00000031 E8CAFFFFFF call $-31h
00000036 CC int3
00000037 33D2 xor edx, edx
00000039 85D2 test edx, edx
0000003B 7511 jnz L_4E
0000003D 8D1D39104000 lea ebx, [$+401039h]
00000043 895DFC mov dword ptr [ebp-4h], ebx
00000046 EB00 jmp L_48
00000048 83C201 add edx, 1h
0000004B FF65FC jmp dword ptr [ebp-4h]
0000004E 52 push edx
0000004F E806000000 call $+Bh
00000054 5F pop edi
00000055 5E pop esi
00000056 5B pop ebx
00000057 C9 leave
00000058 C3 ret
00000059 CC int3
0000005A FF2500204000 jmp dword ptr [$+402000h]
Source code assembled with ml V14.29.30151.0 and object file linked with link V5.12.8078. No any crash.
test was done with 14.33.31630.0
hopefully test asm source was same as i used and isn't reason for crash.
no downloads, so not with same source.