enumerating DLL

Questions about Wine on Linux
Locked
determinator
Level 1
Level 1
Posts: 6
Joined: Tue Dec 03, 2013 3:19 pm

enumerating DLL

Post by determinator »

Hi!

I have a set of special python scripts which extend gcc for debugging / compatibility engineering purposes over a remote connection -- integrating BOTH mingW and Visual Studios C++ symbol mangling, etc. -- but wine is not able to run my scripts quite the same as windows, because the fake dll's do not have the normal PE export table that a real DLL does.... so, help :) How can I get the symbol names and offsets in memory using pure GDB ( not wine-gdb which doesn't run on real windows! )?

My scripts can find path information about loaded DLL's directly from the memory resident PEB and TIB structures of the process being debugged; and and then they can call an external program to get a list of all exported functions. For example...

dumpbin.exe /exports C:\windows\system32\msvcrt.dll

Under windows, this command will correctly return the DLL offsets for the exports, but under wine -- it returns an empty set.

I also tried an in-memory method that is supposed to enumerate dll exports, and I thought wine fake-dll's should be compatible with an in-memory exports listing -- but it wasn't. Even this method returned no symbols... ( I have working code that compiles under visual studio 2009, see: stackoverflow . com/questions/4353116/listing-the-exported-functions-of-a-dll for the basic idea; ask if you want the working code with correct headers and I'll post a functional copy. )

The only other thing I could think of was to modify my python scripts so that when no symbols are returned, to use the WINEDLLS paths, and find the msvcrt.dll.so library in the unix libraries.

nm -g --defined-only /usr/local/lib/wine/msvcrt.dll.so

And I get symbol offsets, type, and names ... just fine ...

00040d60 T MSVCRT_fprintf
000273c0 T _cprintf
00026be0 T _cputs
etc.

But the offsets do not add correctly to the base address of the function when loaded into memory.
So, how do I get the exported symbols and their CORRECT offsets from a built in wine library/fake library directly from the in memory PEB/TIB structures in memory?

Detailed Example of the problem:
A program using msvcrt.dll, gave the following PEB information during a debug session:

path ---- base_address ---- base_address+image_size ---- load_count
...
Z:\home\andrew3\windows\src\dllLinkage\myDll.dll 0x10000000 0x10008000 65535
C:\windows\system32\msvcr90.dll 0x7ebe0000 0x7ec08000 65535
C:\windows\system32\msvcrt.dll 0x7eb40000 0x7ebdb000 3

I traced my program to a place where I explicitly called fprintf(), and it disassembled to:

0x100011ec: call 0x100013f6 ; myDll.dll, calling another place in myDll.dll
...
0x100013f6: jmp *0x100061d0 ;myDll.dll using indirection to function fprintf(): jmp $0x7eb61b1

I originally expected the jump to be to the msvcr90.dll -- because an external app called "dependency walker" says that's what myDll.dll actually linked against; but the indirection table entry *0x100061d0 is not part of msvcr90.dll, it's actually inside myDll.dll's address space itself -- so perhaps it's a ?thunk? or other technique to connect msvcr90 calls to wine's builtin msvcrt?

In any event, the jump ends up going into msvcrt.dll at an offset of 0x7eb61b10 - 0x7eb40000 = 0x21b10 and, yes, it does a printf to stderr as expected...

but, that offset also means the symbol dump from "nm" is useless, because fprintf was supposed to be at offset 0x40d60, not 0x21b10 according to nm. And there is no symbol at offset 0x21b10 or anywhere near it in msvcrt.dll. My program does appear to be using msvcrt.dll -- directly -- but I don't know how to get the right symbol names associated with the addresses GDB returns during disassembly.

Ideas?
oiaohm
Level 8
Level 8
Posts: 1020
Joined: Fri Feb 29, 2008 2:54 am

Re: enumerating DLL

Post by oiaohm »

determinator your plan of using a resolve table to work it out after the fact is stuffed. And its because Wine Is Not a Emulator in the normal sense of the word. I will explain why.

Once we get into ELF libraries all bets are off. http://gcc.gnu.org/onlinedocs/gcc/Funct ... butes.html
ifunc ("resolver") This is a nasty little feature. So that first address nm -g --defined-only returns may only be a resolver. The dlopen/wine loader only runs a resolver once after that places the address returned by the resolver function in the program/library elf function resolve table(indirection table entry you found in myDll.dll this is not windows any more this is ELF) Gcc can in fact do this automatically create functions with slightly different cpu optimization with a resolver wrapper as well.

Also wine also uses redirection like you know there is many libc like libraries in windows. Wine in fact shares functions between all the different versions. Some functions could take you to glibc out side wine completely. Yes the msvcr90 function most likely has resolved twice. First load calls the resolver in msvcr90 that tells the loader to go to msvcrt for printf then msvcrt resolver tells the loader where the function really is. The real functions address does not have to be in exported location with ELF. Worse elf allows application to be suspended then restored with the addresses of external functions moved. Only thing you can be sure not to move is the resolve table in your own application.

You really have no choice if you want to know what function is what you have to have the wine loader working with you. Informing you what functions the application requested and where they end up being pointed. Where it pointed might not even be a wine library like some opengl functions will take you to system opengl. Debuging Linux applications are the same. This is why linux debuggers wraps around applications and tracks the loader. If you are not tracking the loader you don't have a clue what is going on all.

This is why we say Wine is not a emulator. When you look closely enough you see major core differences between windows and wine.

Linux world coming along and connected a debugger to a already running program to work out what has happened does not work too well either. Yes brute forcing to work out what the functions are.

Wine binary format is ELF PE. Welcome to windows debugger nightmare.

determinator the wine project did not create winedbg --gdb for no reason.
determinator
Level 1
Level 1
Posts: 6
Joined: Tue Dec 03, 2013 3:19 pm

Re: enumerating DLL

Post by determinator »

oiaohm wrote:determinator your plan of using a resolve table to work it out after the fact is stuffed. And its because Wine Is Not a Emulator in the normal sense of the word. I will explain why.

Once we get into ELF libraries all bets are off. http://gcc.gnu.org/onlinedocs/gcc/Funct ... butes.html
ifunc ("resolver") This is a nasty little feature. So that first address nm -g --defined-only returns may only be a resolver. The dlopen/wine loader only runs a resolver once after that places the address returned by the resolver function in the program/library elf function resolve table(indirection table entry you found in myDll.dll this is not windows any more this is ELF) Gcc can in fact do this automatically create functions with slightly different cpu optimization with a resolver wrapper as well.
Hmmm..... you're quite knowledgeable; I realize wine is not an emulator -- and I wrote the post because I realized belatedly that accessing the ELF symbols was "stuffed" --- and I appreciate your thoughtful response; I'm merely hoping for an easier solution than doing a grep type operation for the byte sequence of the function being called; but without using wine GDB (see comments near end of post.)

MyDll.dll is actually compiled to run on windows; it's a true windows dll. I'm sorry I didn't make that clear.
It was compiled via: "vs2008 cl /nologo /Zi /EHsc /c myDll.cpp" on the command line. (vs2008 is a batch script to set the environment variables for visual studios 9.).

So, the indirection table I found in myDLL.dll has to be one that would be found even in windows as a true DLL import table -- not a Linux ELF/Dwarf executable; That table, though, because of wine -- has been redirected to wine's/linux .so file implementation -- and as you mention, that may have even happened via a resolver function.

BUT: I don't think it was a resolver function in the example...

I know from debugging linux SO's (NON wine), that dynamic linkage is usually a table of asm jump instructions which *all* point at a resolver function/linker function to begin with -- and that the first time the program tries to execute any function in the table, it (therefore) calls the resolver function instead.
The resolver function, for it's part, chooses the true function to call -- and finds the import table which called it by loading the return address off of the stack; It then executes self modifying code and replaces the "JMP $linker" command in the import table with the true address of the function it should call. Then the linker re-starts the function call, which does it's work; The final is result is that all future calls to the import table go directly to the function from then on, and never call the resolver a second time.

In the case of the debug I did -- the function "fprintf" had already been called once, so It would be VERY unusual (and inefficient) in my experience for that to be the resolver a second time.
Also wine also uses redirection like you know there is many libc like libraries in windows. Wine in fact shares functions between all the different versions. Some functions could take you to glibc out side wine completely. Yes the msvcr90 function most likely has resolved twice. First load calls the resolver in msvcr90 that tells the loader to go to msvcrt for printf then msvcrt resolver tells the loader where the function really is. The real functions address does not have to be in exported location with ELF. Worse elf allows application to be suspended then restored with the addresses of external functions moved. Only thing you can be sure not to move is the resolve table in your own application.
Then, that's what I really want to know how to find -- first; How can I get a list of the resolve table's ORIGINAL name? eg: in a true window's DLL there's clearly an import/resolve table....

Dependency walker ( a true windows app, not wine aware...?) appeared to have an idea of what the function was linked against at compile time -- eg: msvcrt90 + the fprintf... and if I could get the same info as dependency walker, that would likely be sufficient for my purposes; For I don't really care if it is lib-C's printf, or msvcrt's -- what I care is to be able to look up the API name, and have something that can be recognized by my python script -- and also be human readable. ( A hex number stinks... )

In windows and wine, both, it's trivial to check the name vs. the actual address it jumps to against wine's memory map of loaded DLL's (PEB/TIB) to determine when wine has done a redirection...

Also: I never noticed the jmp address changing from call to call in the fprintf()'s so I don't think that the addresses are being changed during execution pauses like you are concerned about -- but only changing once during the first execution of the wine native dll call; for if they were changing, my python scripts would have alerted me for they build debug information dynamically -- and changes would trigger repeated requests for re-definition.
You really have no choice if you want to know what function is what you have to have the wine loader working with you. Informing you what functions the application requested and where they end up being pointed. Where it pointed might not even be a wine library like some opengl functions will take you to system opengl. Debuging Linux applications are the same. This is why linux debuggers wraps around applications and tracks the loader. If you are not tracking the loader you don't have a clue what is going on all.
Sure... and I understand that quite well.
This is why we say Wine is not a emulator. When you look closely enough you see major core differences between windows and wine.

Linux world coming along and connected a debugger to a already running program to work out what has happened does not work too well either. Yes brute forcing to work out what the functions are.

Wine binary format is ELF PE. Welcome to windows debugger nightmare.

determinator the wine project did not create winedbg --gdb for no reason.
[/quote]
:)

I know -- although I am avoiding winegdb, because it's not available on windows, and I am doing the debug sessions on both wine and windows using the same python scripts in real time; winedbg unfortunately introduces bugs and crashes of its own which are not repeatable from run to run, and hence maddening to even attempt to report. At least when I use GDB via a remote proxy compiled for windows (MINGW GDB proxy); I find that my results are repeatable on either platform.
Plus, my python scripts allow me to access dwarf debugging information and the windows pdb server simultaneously -- so I can debug programs compiled with both gcc and visual studio and linked together -- with one debugger.

One question:
Where would I find, in the wine source code, the information about how winegdb tracks the linker?
For I might make a python plugin script that extracts the information directly from memory as the program executes, but I'd like to see how winegdb does it for reference....
determinator
Level 1
Level 1
Posts: 6
Joined: Tue Dec 03, 2013 3:19 pm

Re: enumerating DLL

Post by determinator »

I was able to trace out how the dll loader functions in the source directory dlls/ntdll/loader.c.
Essentially, even though WINE exports found in the elf so's are unusable, the imports thunk table that every windows PE format binary has are still present in memory for the wine builtin libs. I figured out that the binary image for the DLL is mapped into memory, beginning at the address that the TIB/PIB reports as the base address of the DLL. So, merely parsing the DOS header located at the base address of the DLL allowed me to find the ImageDataDirectory and the thunk table, and the originalThunk table for all import symbols; even in the WINE built-in dlls; So it's pretty easy to read the names of all function calls being imported....

A GDB based python script to locate the ImageDataDirectory and locate all import symbols is shown below; The script is running on a 32 bit machine, with GDB compiled for UNIX and remotely connecting to a windows GDB proxy from the MINGW project. IF you're using a 64 bit machine, the sizes of data types might differ -- and the program need to be adjusted. But this is just a quick example of how to do it for pure 32 bit machines...

Code: Select all

import re
import subprocess # for running a command line undname.exe program to de-mangle C++ symbols. 
# I compiled a unix native binary called, undname.x, (not included), from the mingw64 project.

def getName( pc = None, decorated=None ):
	"""
	Convert a disassembled decorated name found at pc into human readable form
	or manually convert a decorated name into human readable form.
	"""
	if not decorated:
		if not pc:
			pc=int( gdb.execute( 'printf "%lu", $eip', to_string=True ) )
		line = gdb.execute( "x/1i "+hex(pc), to_string=True )
		decorated = re.search( "\s+<([^!]+)!(\S+?)(\+[0-9]+)?(?:>)", line )
		if not decorated: return 
		decorated = call.group(2)
	names=subprocess.check_output( ["noerr","undname.x",decorated] ).split("\n")
	if not names[-1]: del names[-1]
	return names[-1]


def getMem(address, type="unsigned int"):
	return int(
		gdb.execute('printf "%d",*('+type+'*)'+str(address),to_string=True)
	)
def getImageDataDirectory( module, i=None ):
	"""
	Find the 32 bit image data directory, IN MEMORY, for windows PE files.
	To get a pointer to the actual data, i can be specified. i= 
	0 Exports, 1 Imports, 2 Resuorces, 3 Excpetion, 4 Security
	5 Base relocation, 6 Debug, 7 Copyright, 8 Unknown
	9 TLS, 10 Load configuration, 11 Bound Import, 12 IAT (Import address table)
	13 Delay import, 14 COM descriptor.
	"""

	try: moduleBase = int( module )
	except: moduleBase = listModule( module )[0]
	if moduleBase & 1: raise ValueError, "Module is not yet mapped."

	if getMem( moduleBase, "unsigned short" ) != 0x5a4d: # dos signature MZ
		raise ValueError,"File missing windows magic, MZ, NT,"

	imageNTHeader = moduleBase + getMem( moduleBase + 0x3C ) # e_lfanew
	if getMem( imageNTHeader ) != 0x4550: raise "NT32 header is missing"
	# fHeader = imageNTHEader + 0x02 # File header.	
	oHeader = imageNTHeader + 0x18 # option header, contains directory.
	if getMem( oHeader,"unsigned short" ) == 0x20b:
		raise ValueError,"Found IMAGE_OPTIONAL_HEADER64 instead of 32."
	if getMem( oHeader,"unsigned short" ) != 0x10b:
		raise ValueError,"Corrupt IMAGE_OPTIONAL_HEADER32, directory not found."
	directory = oHeader + 0x60 # offset of PE ImageDirectory
	if i is None: return directory
	return moduleBase+getMem( directory + 8*i ) # Object's virtual address 

def listImports( module ):
	moduleBase = int( module )
	pImports = getImageDataDirectory( moduleBase, 1 ) #IMAGE_IMPORT_DESCRIPTOR s
	imports=[]

	while True:
		pOriginals = getMem( pImports )
		if not pOriginals: break
		pOriginals += moduleBase
		#timeDateStampe = getMem( pImports + 4 )
		#forwarderChain = getMem( pImports + 8 )
		nameDll = getMemString( getMem( pImports + 12 ) + moduleBase )
		print "import from DLL name=",nameDll
		pFirstThunk = getMem( pImports + 16 ) + moduleBase
		while True:
			original = getMem( pOriginals, "signed int" )
			thunk    = getMem( pFirstThunk, "signed int" )
			if not original: break # Empty record is an end of list indicator.
			hint = getMem( original + moduleBase, "unsigned short" )
			name = getMemString( original + moduleBase + 2 )
			udName = getName( decorated = name )
			if not name: name = dllName+":@"+hex(hint)
			imports.append(
				[	pOriginals, pOriginals+4,  # dummy end address, FIXME later.
					"thunk* "+name, "thunk* "+udName,
					None, [] 
				])
			if thunk != original:
				imports.append(
					[	thunk, thunk+4,
						name+"<--"+nameDll, udName+"<--"+nameDll,
						None, [] 
					])
			pOriginals+=4
			pFirstThunk+=4
		pImports += 20
	return imports

It properly resolves the thunk address to symbols mapping which were compiled by msvc2008 ( vc #9 ) in mydll.dll, and simultaneously the symbols from the gcc compiled WINE builtins. It generally just works.
:) and returns a python list of functions entry addresses and thunks, properly labeled.
oiaohm
Level 8
Level 8
Posts: 1020
Joined: Fri Feb 29, 2008 2:54 am

Re: enumerating DLL

Post by oiaohm »

determinator Exactly you are now working with what the loader creates a little bit. Not trying to go after destination address that could be anywhere but lose.

There is still a issue. When wine get redirecting you will find that the a function that is failing in a dll.so will not line up.

imports thunk table has a nasty big flaw. Can hit you under Windows as well.

LoadLibrary(and relations) combined with GetProcAddress(and releations. Result is a import not listed in the import thunk table.

Yes import thunk table is a half solution. Works on every thing that does not use those two.

The import table is most imported symbols not all. Even so most will be enough a lot of the time. But you do need to add a min a watch for LoadLibrary and GetProcAddress related functions and throw a warning.

determinator the problem to beware of is Linux application hibernation/suspend. There are a few userspace variations that could catch your users by horror. Yes resolver processing only happens once. What about the horible case that application restore from suspend finds the address it was using are in use. Welcome to ugly. Yes there is a second way your resolve tables can be messed with. Yes the result can crash some applications but the applications/libraries that don't crash have a more confusing tracking.
determinator
Level 1
Level 1
Posts: 6
Joined: Tue Dec 03, 2013 3:19 pm

Re: enumerating DLL

Post by determinator »

Hmmm....
Yes. returning from suspend would definitely allow problems to arise.... Thankfully, I'm not trying to do anything that complicated yet. My Linux machine never hibernates.... But I will keep that in mind for the future; once I get the basic debugging working satisfactorily, and am familiar enough with WINE to get around quickly -- I'll tackle that kind of problem.

For now:
I've mostly won as far as linux native DLL's -- for under WINE, all of them list symbols in the import thunk tables except for shlwapi.dll and comctl42.dll; which have invalid pointers to OriginalFirstThunk, and FirstThunk because the export by ordinals not name. But they are the only exceptions.

I am able to reliably trace how the wine builtins that are being called -- in a way which is compatible with debugging the binaries on a true windows machine using the same GDB debugger, and python scripts. :) ( HAPPINESS IS A GOOD DEBUGGER. )

BUT: There a problem, though, that I am hitting which is very bothersome with native windows dll's.
Some libraries, like msvc90.dll , are exported by ordinal number instead of by name -- just like WINE native libraries shlwapi.dll and comctl42.dll -- but I am not able to access the ordinal numbers except in WINE built-in DLL's.

EG: the import thunk struct is the same for both ordinal importing and name importing -- only the meaning changes; so I should be able to access it in a mapped file....

http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
http://www.ntcore.com/files/inject2it.htm

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD OriginalFirstThunk; // a pointer to IMAGE_THUNK_DATA.Address of data
DWORD TimeDateStamp; // Always 0x00000000 in wine images I've seen...
DWORD ForwarderChain; // ... 0x00000000
DWORD Name; // The DLL name being imported from
DWORD FirstThunk; // pointer to IMAGE_THUNK_DATA.Function
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; // The ordinal value - 1 CAN go in this spot, IF this value >= 32768, then four bytes packed as the ordinal number should be here -- but export by ordinal doesn't work in wine.
BYTE Name[1]; // Export by name puts the name here, and zero is generally found in the HInt.
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

typedef struct _IMAGE_THUNK_DATA {
union {
PDWORD Function; // Pointer to imported function/data (true thunk)
PIMAGE_IMPORT_BY_NAME AddressOfData; // packed as an ordinal number + a pointer to string.
} u1;
} IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA;


If I had the ordinals(or the "hint" mentioned above) mapping the names would be trivial, since I have the .def files ( and even the .pdb files for that matter) for the msvcr90.dll , etc. as microsoft provides them on demand for download .... but the ordinals aren't in the memory map loaded by wine from the PE so I'm stuck.

OTOH: dependency walker is able to find them in the original PE executable file -- so it's not a matter of them NOT being there, it's a matter of them not being mapped into memory by WINE....
Apparently WINE only maps the OriginalFirstThunks into memory when mapping by name -- but it doesn't when the fields contain the ordinals instead for a TRUE windows binary -- but, it DOES do it when mapping in a builtin library.

Why is this the case ?
Is there a way to change this behavior?

Alternatively -- but not a good fix:
I installed dia2dump.exe; In order to make it work, I had to execute "wine regsrv diaguid.dll" to register the dll in the registry for use without a path as dia2dump requires; and I had to set an environment variable to tell dia2dump where the microsoft debugger files would be cached (.pdb) by my gdb python scripts (eg: the same unix directory my gdb debugger runs in. )
eg: bash script# export _NT_SYMBOL_PATH="srv*Z:\\home\\andrew3\\windows\\src\\gdb"

... And I figured out how to exctract the mdsl.microsoft.com path from any dll loaded into memory in order to request the debugging symbols directly from Microsoft; so I now have my Linux program automatically downloading the .pdb files for msvcr90.dll --- and I can dump the symbols using dia2dump -- just like nm in unix for ELF/DWARF2 binaries. BUt it's going to have similar problems....

sigh..... if I could figure out how to get WINE to map the original first thunk, this would all be a moot point.... but I'm not sure where that particular bit of code is found at in the WINE sources.
oiaohm
Level 8
Level 8
Posts: 1020
Joined: Fri Feb 29, 2008 2:54 am

Re: enumerating DLL

Post by oiaohm »

determinator I would say possible missing test case.

Does dependency walker behave differently under wine and on windows when handling native libraries.
Why is this the case ?
Most likely because wine is old is the case. Some of this mapping behavior has changed over time inside Windows. There is every chance the ELF loader has been updated and the PE loader has not been so creating the different.

The wine loader code rarely sees patches or even open bug reports.

Wine project main objective is not to require using any winetricks options. So there might be some minor resistance to fixing. So since wine has MSVCR90.dll as dll.so if a function is missing that requires you to install MSVCR90.dll then a bug report should be place about it.

Be warned inject2it page "Runtime Import Table Injection" can land on the bad side of Linux security inside wine. Basically how to get your application terminated without notice attempt to alter a different applications memory that your application did not start. Windows altering an application memory that your application did not start is permitted. Just remember this if you debugger did not start the application watching is fine but modifying with be rolling the dice against the Linux Security Module you have loaded. determinator gdb.exe inside wine will not be knowing to the host OS security to have special exceptions. There are still reasons to use the winedbg when able. It is able to talk to wineserver and other things and avoid stepping on particular set of mines Linux security systems throw up. Even so winedbg does need work.

determinator your workaround is always going to have limitations. Limitations that are going to come back and bite some people on some platforms.

I missed responding to this.
winedbg unfortunately introduces bugs and crashes of its own which are not repeatable from run to run, and hence maddening to even attempt to report.
Don't be too sure. This sounds like race conditions. winedbg needs more testcases. Like not repeatable from run to run is the only form of bug wine developers take on. Does the same fault appear if it run like 50-100 times at least twice. If so this can be turned into a slow test case to give to wine developers to find what is going wrong here. The big thing they need is something they can run and prove they have fixed it. Ok it might take 4 to 8 hours of PC time to confirm the fix but so be it. Just as long as it does not take 4 to 8 hours of human time. Automated test case they go to bed leave PC on.

Most people get annoyed with race condition bugs and don't report them. Most people forget repeatability for a developer does not have to be perfectly 1 after another. Just that it must repeat inside a particularly number of tries.
determinator
Level 1
Level 1
Posts: 6
Joined: Tue Dec 03, 2013 3:19 pm

Re: enumerating DLL

Post by determinator »

oiaohm wrote:Does dependency walker behave differently under wine and on windows when handling native libraries.
Yes and No. No -- in the sense that Dependency walker is able to find the ordinal number of each function being imported, under wine or windows equally well. Yes in the sense that it had trouble detecting some built in DLL's, when they exported by ordinals.

The fact that DW could correctly identify the native windows msvcrt90's ordinals -- even when the DLL has been stripped of function names, and even when a memory scan by GDB of the native DLL after loading showed that segments of the PE file eg: those containing the information necessary to decode the ordinal numbers ... were not mapped into memory by WINE / or else the pointer that the loader left behind was invalid -- suggests that dependency walker is not using the windows documented way to obtain the information from an already memory mapped process image / thread; but rather, I'd bet DW is probably getting the information directly from the executable file on disk. But I haven't source code for DW to prove the point... and I haven't reverse engineered it... but I do know: Windows PE format files document two ways to get the information -- one from the disk file, and another from the memory mapped image. eg: there are pointers to indicate where the ordinal table is located at in the disk image in addition to pointers showing where to find them in the memory (RVA values). So DW could be just using a work around.
Most likely because wine is old is the case. Some of this mapping behavior has changed over time inside Windows. There is every chance the ELF loader has been updated and the PE loader has not been so creating the different.
I'm pretty sure that importing by ordinal number, using the original first thunk and first thunk tables are documented features of the original windows NT specification; eg: Even back as far as windows 95 and windows NT. Although the documentation was so bad at the time that the author of the following article wasn't sure you actually "could" import by ordinal numbers -- but (even so) he still lists out the exact same data structures ( Characteristics AKA orignalFirstThunk, and FirstThunk) as far back as March, 1994 to do the importing.

http://msdn.microsoft.com/en-us/library/ms809762.aspx

I have found no article showing any other format or technique which contradicts or supplements the article I posted previously -- eg: which shows that import by ordinal number is caused whenever the high bit is found set in a thunk pointer; eg: if a the 32 bit pointer is pointing to the high half of (virtual) memory, then one merely and's the pointer with 0x7FFFFFFF, and the result will be an ordinal number.

According to WINE documentation, wine does not support PRE-NT binary formats -- so I really don't see a reason that the WINE loader should be using a technique from older Windows apps. than the original NT/95 PE format which is the one I'm trying to use. Although it's true that 64 bit computers have (since that time) required the PE file to adapt the data word size in the structures -- those changes also requires a different header with a signature which isn't present in the files I'm debugging; for I'm strictly working with 32 bit windows executables, on a true 32 bit computer -- so there's no reason that format changes ought to be affecting the linking process.... ?

The only confounding issue which came up in my search is that Borland's linker did not support the OriginalFirstThunk table in the PE executable -- so perhaps the WINE implementers decided it wasn't necessary to actually support the table in memory? eg: since not all compilers created it? -- although, if that's the case -- it's annoying -- for that table's the only truly portable way to track import functions when ordinals are involved *after* the linking stage of the loader has completed. So -- there are only two choices for me, parse the disk image -- or else, find where in the WINE loader the source code omits to map the import table when import by ordinal is happening -- and patch the source code for my purposes. (That's what I'm likely to do.)
Wine project main objective is not to require using any winetricks options. So there might be some minor resistance to fixing. So since wine has MSVCR90.dll as dll.so if a function is missing that requires you to install MSVCR90.dll then a bug report should be place about it.
I installed MSVCR90 because I was using dependency walker to track which DLL's I needed to get off the program's disk to make it function; eg: dependency walker did not find MSVCR90 when it scanned the program I wanted to run without the native DLL installed but listed that library as "missing"; Once I began debugging with the native DLL -- that's how I isolated the fact that export by ordinal DLL's are handled differently under WINE than built-in DLL's are; Although, there are many WINE built in's that dependency walker DID detect, and when it detected one using export by ordinal -- it correctly found the ordinal's being used.

You've brought up an interesting point.... I'll have to try and remove MSVCR90.dll to see what the original first thunk, and original thunk tables look like when running GDB on the built-in dll. Perhaps my GDB python scripts will be able to read the ordinals once the built-in is loaded and mapped into memory.... although that doesn't solve the basic problem for other DLL's that don't have a wine built-in equivalent, eg: but are still mapped by ordinal -- nor does it help me figure out why dependency walker couldn't detect msvcr90's existence when import by ordinals is being used. Although, I suspect the problem is related to the ordinal import technique.
Be warned inject2it page "Runtime Import Table Injection" can land on the bad side of Linux security inside wine. Basically how to get your application terminated without notice attempt to alter a different applications memory that your application did not start.
I don't actually need to inject into the import table at run time; I just cited that article which used the technique because it studied the import table rather thoroughly and documented it well. (Although, on my test computer -- I am able to circumvent the linux security in about three ways that I have proven already by testing, so if I want to use the technique -- I can. It just takes some extra steps to make GDB smart enough to not trip the security. But generally, since I have Visual Studios on my computer anyway -- it's simpler just to write a wrapper DLL that allows me to write arbitrary test code and forward actual calls to the original DLL, rather than bother doing it from GDB. )
determinator your workaround is always going to have limitations. Limitations that are going to come back and bite some people on some platforms.
Every solution has limitations; so I don't see how that makes a difference. The information I'm providing will (conversely) help some people on some platforms who couldn't use the stock solution.... which is limited, too.
Don't be too sure. This sounds like race conditions. winedbg needs more testcases. Like not repeatable from run to run is the only form of bug wine developers take on. Does the same fault appear if it run like 50-100 times at least twice. If so this can be turned into a slow test case to give to wine developers to find what is going wrong here. The big thing they need is something they can run and prove they have fixed it. Ok it might take 4 to 8 hours of PC time to confirm the fix but so be it. Just as long as it does not take 4 to 8 hours of human time. Automated test case they go to bed leave PC on.
That's a nice thought -- but it's impractical in most of the cases I have tried to work on in the past.

The supposed race condition is caused by a piece of copyrighted commercial software that I can't give to the wine developers to test with; Nor will they be buying it.... The failure was caused after having entered a certain set of function calls that takes a substantial amount of time to arrive at and which I don't understand well enough to be able to show the likely causes for a race. eg: It's not like I can isolate the problem in a way which can be handed off to people without my exact set up -- and my attempts to report it some years ago proved that the developers at Wine couldn't replicate it. It's not that I blame them for closing the report -- it's just that reality is harsh when it comes to intermittent failures. If I ran the same calls with just GDB -- and not on top of wine-GDB the problem didn't occur... SO -- I was **SOL*** either way. Without WINE-GDB I was blind, with it -- I couldn't run the program to completion.
oiaohm
Level 8
Level 8
Posts: 1020
Joined: Fri Feb 29, 2008 2:54 am

Re: enumerating DLL

Post by oiaohm »

determinator there is some bad news. Wine goes back to ECMA specification of windows or basically windows 3.11 or Win32s implementation of PE. Yes not a NT implementation of PE. Windows 9x is when PE implementation on windows come into alignment.

Original windows NT specification and the ECMA spec don't agree.
According to WINE documentation, wine does not support PRE-NT binary formats
This is not true once you get into legacy code of wine. Wine support NE to a point for 16 bit support that is pre NT. Our objective is to move to not having any of the old PRE-NT stuff left. But since the PRE-NT stuff was in wine there is chance that fragments are still around places.

Yes windows original PE loader is from the ECMA specification hopefully fully updated to be NT compatible(possibly not and from the errors you are having completely not). The dll.so/ PE ELF is from NT specification.

If you are able to find where the PE Loader is bugged the patch most likely will be accepted mainline. It will be a legacy code issue from the first implementation of the PE Loader. 1995 wine was still using pure ELF binaries for native code. The PE ELF hibred for compatibility comes latter. Yes the appearance of PE ELF is when the idea of NT formats appear in wine.

Basically you are looking for a 15 year+ old bit of buggy code.
The supposed race condition is caused by a piece of copyrighted commercial software that I can't give to the wine developers to test with; Nor will they be buying it....
Codeweavers and some other groups funding developers on wine do support buying so much software and even sign NDA. Of course if a demo version does the same thing they will take that first.

Something to remember is developers of wine have limited time to set up automated testing. So they try a handful of times without automated testing. They will try into the 50's and 100 hundreds if they are provided with a script todo it.

I have seen a few annoying bugs that have been closed that have been reopened when developer has been giving a test system to detect it.
determinator
Level 1
Level 1
Posts: 6
Joined: Tue Dec 03, 2013 3:19 pm

Re: enumerating DLL

Post by determinator »

Hmmm.... There's something more subtle going on than I first thought.

Dependency walker has been giving mildly misleading information -- supplying ordinals when names are available, and vice versa; although it always gives a correct value -- it hasn't always been correctly identifying when ordinals are being exported/imported vs. names; depending on where in the tree the import is located at; eg: I get inconsistent import results for the same dll depending on whether I start at the PE executable which imports that DLL, and then traverse into the dll from Dependency Walker's menu -- or whether I load the DLL directly.

So, It seems that part of the problem is a bug of some kind in dependency walker / wine interaction ; and I'm going to set it aside for now and concentrate on the image in memory straight; I have solved the problem with getting the ordinal numbers from memory... at least when the compiler gives a originalFirstThunk ; basically I can get the info from it -- otherwise, I need to parse the file image.
oiaohm
Level 8
Level 8
Posts: 1020
Joined: Fri Feb 29, 2008 2:54 am

Re: enumerating DLL

Post by oiaohm »

determinator maybe you are expecting something that does not exist in wine intentionally.

https://github.com/apitrace/apitrace
For D3D10 and higher you really must use apitrace trace -a DXGI .... This is because D3D10-11 API span many DLLs which depend on each other, and once a DLL with a given name is loaded Windows will reuse it for LoadLibrary calls of the same name, causing internal calls to be traced erroneously. apitrace trace solves this issue by injecting a DLL dxgitrace.dll and patching all modules to hook only the APIs of interest.
See nice big security hole in windows. This defect does not exist in wine. Result is that the two dll one loaded by an application then traced into then when loaded by your application have different address information under wine.

Dependency walker is most likely making the presume that it is running in a windows loader environment where loaded dlls have identical addresses. Yes this is one of the secuirty holes that makes windows swiss cheese.
Locked