Skip to content

Instantly share code, notes, and snippets.

@rygorous
Last active December 24, 2015 11:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rygorous/6790191 to your computer and use it in GitHub Desktop.
Save rygorous/6790191 to your computer and use it in GitHub Desktop.
Dump of file test.obj
File Type: COFF OBJECT
RELOCATIONS #1
Symbol Symbol
Offset Type Applied To Index Name
-------- ---------------- ----------------- -------- ------
00000006 ADDR32 00000000 9 $jump@f <--- ADDR32 for this is not OK!
00000013 ADDR64 00000000 00000000 A $L0@f
0000001B ADDR64 00000000 00000000 B $L1@f
Summary
0 .data
60 .debug$S
23 .text
_TEXT SEGMENT
f PROC
and eax, 1
jmp qword ptr $jump@f[rax*8] ; abs. data references are tricky in x64 code!
$L0@f:
xor eax, eax
ret
$L1@f:
mov eax, 1
ret
$jump@f:
dq $L0@f
dq $L1@f
f ENDP
_TEXT ENDS
END
; cl /c /nologo /O2 /FAs bug2.c (x64)
; Listing generated by Microsoft (R) Optimizing Compiler Version 17.00.60610.1
include listing.inc
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC f
EXTRN __ImageBase:BYTE
; Function compile flags: /Ogtpy
; File c:\test\bug2.c
; COMDAT f
_TEXT SEGMENT
x$ = 8
f PROC ; COMDAT
; 3 : switch (x & 7) { // code doesn't matter, just something to get a jump table
mov eax, ecx
and eax, 7
cmp eax, 7
ja SHORT $LN6@f
lea r8, OFFSET FLAT:__ImageBase
cdqe
mov edx, DWORD PTR $LN10@f[r8+rax*4]
add rdx, r8
jmp rdx
$LN5@f:
; 4 : case 0: return x + 1;
lea eax, DWORD PTR [rcx+1]
; 11 : }
ret 0
$LN4@f:
; 5 : case 1: return x * 3;
lea eax, DWORD PTR [rcx+rcx*2]
; 11 : }
ret 0
$LN3@f:
; 6 : case 2: return x * 5;
lea eax, DWORD PTR [rcx+rcx*4]
; 11 : }
ret 0
$LN2@f:
; 7 : case 3: return x - 1;
lea eax, DWORD PTR [rcx-1]
; 11 : }
ret 0
$LN1@f:
; 8 : case 4: case 5: case 6: case 7: return x;
mov eax, ecx
; 11 : }
ret 0
$LN6@f:
; 9 : }
; 10 : return 0;
xor eax, eax
; 11 : }
ret 0
npad 2
$LN10@f:
DD $LN5@f
DD $LN4@f
DD $LN3@f
DD $LN2@f
DD $LN1@f
DD $LN1@f
DD $LN1@f
DD $LN1@f
f ENDP
_TEXT ENDS
END
int f(int x)
{
switch (x & 7) { // code doesn't matter, just something to get a jump table
case 0: return x + 1;
case 1: return x * 3;
case 2: return x * 5;
case 3: return x - 1;
case 4: case 5: case 6: case 7: return x;
}
return 0;
}
C:\test>ml64 /c /nologo bug2.asm
Assembling: bug2.asm
bug2.asm(23) : error A2006:undefined symbol : FLAT
---> So edit by hand to remove "FLAT:", producing bug2b.asm
Now compare relocations in output OBJ for bug2.c ("cl /c /O2 /nologo bug2.c") vs bug2b.asm ("ml64 /c /nologo bug2b.asm"):
C:\test>dumpbin /relocations bug2.obj
Microsoft (R) COFF/PE Dumper Version 11.00.60610.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file bug2.obj
File Type: COFF OBJECT
RELOCATIONS #3
Symbol Symbol
Offset Type Applied To Index Name
-------- ---------------- ----------------- -------- ------
0000000D REL32 00000000 F __ImageBase
00000017 ADDR32NB 00000000 9 $LN10
00000038 ADDR32NB 00000000 A $LN5
0000003C ADDR32NB 00000000 B $LN4
00000040 ADDR32NB 00000000 C $LN3
00000044 ADDR32NB 00000000 D $LN2
00000048 ADDR32NB 00000000 E $LN1
0000004C ADDR32NB 00000000 E $LN1
00000050 ADDR32NB 00000000 E $LN1
00000054 ADDR32NB 00000000 E $LN1
Summary
64 .debug$S
2F .drectve
58 .text
vs.
C:\test>dumpbin /relocations bug2b.obj
Microsoft (R) COFF/PE Dumper Version 11.00.60610.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file bug2b.obj
File Type: COFF OBJECT
RELOCATIONS #1
Symbol Symbol
Offset Type Applied To Index Name
-------- ---------------- ----------------- -------- ------
0000000D REL32 00000000 A __ImageBase
00000017 ADDR32 00000000 C $LN10@f
00000038 ADDR32 00000000 D $LN5@f
0000003C ADDR32 00000000 E $LN4@f
00000040 ADDR32 00000000 F $LN3@f
00000044 ADDR32 00000000 10 $LN2@f
00000048 ADDR32 00000000 11 $LN1@f
0000004C ADDR32 00000000 11 $LN1@f
00000050 ADDR32 00000000 11 $LN1@f
00000054 ADDR32 00000000 11 $LN1@f
Summary
0 .data
60 .debug$S
28 .drectve
58 .text
NOTE how these are all ADDR32 (broken) instead of ADDR32NB (relative to image base). The compiler
clearly expects the LEA for ImageBase to be emitted as RIP-relative, then the remaining offsets
to be ADDR32NB (see e.g. addition of r8 in the jump table access).
Can manually fix the jump table by making all offsets IMAGEREL:
$LN10@f:
DD IMAGEREL $LN5@f
DD IMAGEREL $LN4@f
DD IMAGEREL $LN3@f
DD IMAGEREL $LN2@f
DD IMAGEREL $LN1@f
DD IMAGEREL $LN1@f
DD IMAGEREL $LN1@f
DD IMAGEREL $LN1@f
(rest as before). Call this "bug2c.asm". This assembles to an object file with these relocations:
Dump of file bug2c.obj
File Type: COFF OBJECT
RELOCATIONS #1
Symbol Symbol
Offset Type Applied To Index Name
-------- ---------------- ----------------- -------- ------
0000000D REL32 00000000 A __ImageBase
00000017 ADDR32 00000000 C $LN10@f
00000038 ADDR32NB 00000000 D $LN5@f
0000003C ADDR32NB 00000000 E $LN4@f
00000040 ADDR32NB 00000000 F $LN3@f
00000044 ADDR32NB 00000000 10 $LN2@f
00000048 ADDR32NB 00000000 11 $LN1@f
0000004C ADDR32NB 00000000 11 $LN1@f
00000050 ADDR32NB 00000000 11 $LN1@f
00000054 ADDR32NB 00000000 11 $LN1@f
Note how the jump table is now correct, but the reference to the jump table is still broken.
The "natural" fix from where I'm standing would be to change the instruction from
mov edx, DWORD PTR $LN10@f[r8+rax*4]
to
mov edx, DWORD PTR (IMAGEREL $LN10@f)[r8+rax*4]
But alas, this produces:
C:\test>ml64 /nologo /c bug2d.asm
Assembling: bug2d.asm
bug2d.asm(25) : error A2102:cannot add memory expression and code label
Unless there's some other construct for this case that I'm not aware of, there seems to be
no way to assemble that jump table reference in a way that actually works. That is the actual
problem I was running into.
And either way, the fact that the listing produced by the compiler doesn't assemble with ML64,
and even after removing the "FLAT:" reference assembles to something that looks good but means
something different (and in particular has broken relocations) seems bad.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment