Hi everybody,
I noticed that, when a builtin dll (.so) is loaded, the SizeOfImage field in the OptionalHeader is not updated and stays equal to page_size (see winebuild, spec32).
I need to get the correct value in SizeOfImage.
I thought about updating that field in map_dll@library/loader.c, but don't know how to get the size of the image.
Could anyone help me ? I'm not familiar with the PE loader.
Thanks in advance,
Laurent Pinchart
On Tue, May 28, 2002 at 12:08:46AM +0200, Laurent Pinchart wrote:
Hi everybody,
I noticed that, when a builtin dll (.so) is loaded, the SizeOfImage field in the OptionalHeader is not updated and stays equal to page_size (see winebuild, spec32).
I need to get the correct value in SizeOfImage.
I thought about updating that field in map_dll@library/loader.c, but don't know how to get the size of the image.
Could anyone help me ? I'm not familiar with the PE loader.
You can't find out the size of the image from the libdl API calls except going into heavy ELF parsing.
winebuild does not really know either.
My suggestion would be to use a heuristic like the one below. However it is not perfect, the sizes are too large sometimes.
Ciao, Marcus
Copyright: LGPL
Index: loader.c =================================================================== RCS file: /home/wine/wine/library/loader.c,v retrieving revision 1.15 diff -u -r1.15 loader.c --- loader.c 22 May 2002 21:32:49 -0000 1.15 +++ loader.c 29 May 2002 07:54:16 -0000 @@ -21,6 +21,7 @@ #include "config.h" #include "wine/port.h"
+#include <stdio.h> #include <assert.h> #include <ctype.h> #include <stdlib.h> @@ -155,19 +156,23 @@
/* adjust an array of pointers to make them into RVAs */ -static inline void fixup_rva_ptrs( void *array, void *base, int count ) +static inline void fixup_rva_ptrs( void *array, void *base, int count, int *rmax) { void **ptr = (void **)array; while (count--) { - if (*ptr) *ptr = (void *)((char *)*ptr - (char *)base); + if (*ptr) { + int diff = ((char *)*ptr - (char *)base); + *ptr = (void *)diff; + if (diff > *rmax) *rmax = diff; + } ptr++; } }
/* fixup RVAs in the resource directory */ -static void fixup_resources( IMAGE_RESOURCE_DIRECTORY *dir, char *root, void *base ) +static void fixup_resources( IMAGE_RESOURCE_DIRECTORY *dir, char *root, void *base, int *rmax ) { IMAGE_RESOURCE_DIRECTORY_ENTRY *entry; int i; @@ -176,11 +181,11 @@ for (i = 0; i < dir->NumberOfNamedEntries + dir->NumberOfIdEntries; i++, entry++) { void *ptr = root + entry->u2.s3.OffsetToDirectory; - if (entry->u2.s3.DataIsDirectory) fixup_resources( ptr, root, base ); + if (entry->u2.s3.DataIsDirectory) fixup_resources( ptr, root, base, rmax ); else { IMAGE_RESOURCE_DATA_ENTRY *data = ptr; - fixup_rva_ptrs( &data->OffsetToData, base, 1 ); + fixup_rva_ptrs( &data->OffsetToData, base, 1, rmax ); } } } @@ -197,6 +202,7 @@ BYTE *addr, *code_start, *data_start; size_t page_size = getpagesize(); int nb_sections = 2; /* code + data */ + int rmax = 0;
size_t size = (sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) @@ -238,7 +244,7 @@ nt->OptionalHeader.SizeOfUninitializedData = 0; nt->OptionalHeader.ImageBase = (DWORD)addr;
- fixup_rva_ptrs( &nt->OptionalHeader.AddressOfEntryPoint, addr, 1 ); + fixup_rva_ptrs( &nt->OptionalHeader.AddressOfEntryPoint, addr, 1, &rmax );
/* Build the code section */
@@ -267,9 +273,9 @@ if (dir->Size) { IMAGE_IMPORT_DESCRIPTOR *imports = (void *)dir->VirtualAddress; - fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 ); + fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 , &rmax); /* we can fixup everything at once since we only have pointers and 0 values */ - fixup_rva_ptrs( imports, addr, dir->Size / sizeof(void*) ); + fixup_rva_ptrs( imports, addr, dir->Size / sizeof(void*), &rmax ); }
/* Build the resource directory */ @@ -278,8 +284,8 @@ if (dir->Size) { void *ptr = (void *)dir->VirtualAddress; - fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 ); - fixup_resources( ptr, ptr, addr ); + fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 , &rmax); + fixup_resources( ptr, ptr, addr, &rmax ); }
/* Build the export directory */ @@ -288,14 +294,17 @@ if (dir->Size) { IMAGE_EXPORT_DIRECTORY *exports = (void *)dir->VirtualAddress; - fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 ); - fixup_rva_ptrs( (void *)exports->AddressOfFunctions, addr, exports->NumberOfFunctions ); - fixup_rva_ptrs( (void *)exports->AddressOfNames, addr, exports->NumberOfNames ); - fixup_rva_ptrs( &exports->Name, addr, 1 ); - fixup_rva_ptrs( &exports->AddressOfFunctions, addr, 1 ); - fixup_rva_ptrs( &exports->AddressOfNames, addr, 1 ); - fixup_rva_ptrs( &exports->AddressOfNameOrdinals, addr, 1 ); + fixup_rva_ptrs( &dir->VirtualAddress, addr, 1, &rmax ); + fixup_rva_ptrs( (void *)exports->AddressOfFunctions, addr, exports->NumberOfFunctions, &rmax ); + fixup_rva_ptrs( (void *)exports->AddressOfNames, addr, exports->NumberOfNames, &rmax ); + fixup_rva_ptrs( &exports->Name, addr, 1, &rmax ); + fixup_rva_ptrs( &exports->AddressOfFunctions, addr, 1, &rmax ); + fixup_rva_ptrs( &exports->AddressOfNames, addr, 1, &rmax ); + fixup_rva_ptrs( &exports->AddressOfNameOrdinals, addr, 1, &rmax ); } + + fprintf(stderr,"rmax (sizeofimage) is %ld\n",rmax); + return addr; #else /* HAVE_MMAP */ return NULL;
Hi everybody,
I noticed that, when a builtin dll (.so) is loaded, the SizeOfImage field in the OptionalHeader is not updated and stays equal to page_size (see winebuild, spec32).
I need to get the correct value in SizeOfImage.
I thought about updating that field in map_dll@library/loader.c, but don't know how to get the size of the image.
Could anyone help me ? I'm not familiar with the PE loader.
You can't find out the size of the image from the libdl API calls except going into heavy ELF parsing.
winebuild does not really know either.
My suggestion would be to use a heuristic like the one below. However it is not perfect, the sizes are too large sometimes.
Ciao, Marcus
I thought about modifying winebuild to add
char _end[]; nt_headers->OptionalHeader.SizeOfImage = pe_header - _end;
in the library initialisation function. This is small, fast and probably more accurate.
Does anyone have any comment about that ?
Laurent Pinchart
I thought about modifying winebuild to add
char _end[]; nt_headers->OptionalHeader.SizeOfImage = pe_header - _end;
in the library initialisation function. This is small, fast and probably more accurate.
Does anyone have any comment about that ?
don't you mean extern char _end[] instead ? there's nothing currently which ensures that pe_header is at the start of the code (it relies on the fact it's the first on gcc/ld link option, and that ld allocates slots for compilation units in the order they are expressed) => this may become a porting issue I'm not sure either that _end is completly portable?
using this technique (with _start, _etext, _edata, _bss_start, _end) we could also compute the "real" size of code, data, and bss which should be of some interest (even if data would be a bit more difficult, since we need to take care of the rsrc part)
beware also that those fields must be aligned on the section boundary (see SectionAlignment in optional header)
A+
On Wednesday 29 May 2002 22:40, Eric Pouech wrote:
I thought about modifying winebuild to add
char _end[]; nt_headers->OptionalHeader.SizeOfImage = pe_header - _end;
in the library initialisation function. This is small, fast and probably more accurate.
Does anyone have any comment about that ?
don't you mean extern char _end[] instead ?
Yes, sorry.
there's nothing currently which ensures that pe_header is at the start of the code (it relies on the fact it's the first on gcc/ld link option, and that ld allocates slots for compilation units in the order they are expressed) => this may become a porting issue I'm not sure either that _end is completly portable?
I don't know either. We could instead add a linker script which would set a symbol to the difference between the end and the start of the file. That should be more portable.
using this technique (with _start, _etext, _edata, _bss_start, _end) we could also compute the "real" size of code, data, and bss which should be of some interest (even if data would be a bit more difficult, since we need to take care of the rsrc part)
One thing at a time please :-)
I tried the code described above, but it unfortunately doesn't work for all dlls. It returns a correct value in some cases (for small dlls it seems), and a huge negative value around 0xc0000000 in other cases (for bigger dlls).
I check the pe_header and _end symbols using nm and they are correct everytime.
I then checked the assembly code generated by gcc, and found out that _end - pe_header is computed by reading 2 valus in the .got section and substracting them.
Could anyone help me ?
Should I try with a linker script ?
Laurent Pinchart
Laurent Pinchart laurent.pinchart@skynet.be writes:
I then checked the assembly code generated by gcc, and found out that _end - pe_header is computed by reading 2 valus in the .got section and substracting them.
Could anyone help me ?
Should I try with a linker script ?
Maybe you should tell us why you need a correct value in there, it may be possible to find a simpler solution.
I then checked the assembly code generated by gcc, and found out that _end - pe_header is computed by reading 2 valus in the .got section and substracting them.
Could anyone help me ?
Should I try with a linker script ?
Maybe you should tell us why you need a correct value in there, it may be possible to find a simpler solution.
SafeDisc implements GetProcAddress internally, and verifies that the returned address lies between ImageBase and ImageBase + SizeOfImage. It fails if it doesn't.
I could use a big value (0x80000000) for SizeOfImage but I don't like that.
Laurent Pinchart
Laurent Pinchart laurent.pinchart@skynet.be writes:
SafeDisc implements GetProcAddress internally, and verifies that the returned address lies between ImageBase and ImageBase + SizeOfImage. It fails if it doesn't.
In that case the easy way would be to take the highest address from the export table and use that. It would actually be a pretty good approximation. The correct way would be a linker script; this is relatively easy to make work on one platform, but a bit more painful to make work for all platforms.
On Wed, 29 May 2002, Laurent Pinchart wrote:
SafeDisc implements GetProcAddress internally, and verifies that the returned address lies between ImageBase and ImageBase + SizeOfImage. It fails if it doesn't.
A warning: if I remember right, once you pass this hurdle, you'll encounter a check so hideous and nasty that if you can solve it without running afoul of the DMCA *and* have it accepted into Wine, I'd just about pronounce you a grand-master top-class super-elite hacker. Good luck...
Laurent Pinchart laurent.pinchart@skynet.be writes:
I tried the code described above, but it unfortunately doesn't work for all dlls. It returns a correct value in some cases (for small dlls it seems), and a huge negative value around 0xc0000000 in other cases (for bigger dlls).
Most likely the problem is with dlls that aren't properly separated yet. Since they don't use -Bsymbolic _end will resolve to the wrong symbol.
I looked into my old linker script stuff but I'm afraid it won't help in this case. We used it mostly to put the PE header at the right place but the current solution should work just as well for that. So I'd suggest using _end and related symbols, maybe with a configure check, and fall back to some hackish solution for non-separated dlls.
On Friday 31 May 2002 19:42, Alexandre Julliard wrote:
Laurent Pinchart laurent.pinchart@skynet.be writes:
I tried the code described above, but it unfortunately doesn't work for all dlls. It returns a correct value in some cases (for small dlls it seems), and a huge negative value around 0xc0000000 in other cases (for bigger dlls).
Most likely the problem is with dlls that aren't properly separated yet. Since they don't use -Bsymbolic _end will resolve to the wrong symbol.
I looked into my old linker script stuff but I'm afraid it won't help in this case. We used it mostly to put the PE header at the right place but the current solution should work just as well for that. So I'd suggest using _end and related symbols, maybe with a configure check, and fall back to some hackish solution for non-separated dlls.
I computing SizeOfImage by looking for the highest address in the export table. This works for all separated dlls. As you stated _end should work for all separated dlls, so it should be the way to go, with a fallback on export table highest address lookup. When all dlls will be properly separated the fallback won't be necessary anymore.
I still have a problem with DeviceIoControl, which is exported by kernel32.dll but located in ntdll.dll. I moved it to kernel32.dll as a wrapper and let the code in ntdll.dll. This is hackish, but works, so I can continue with SafeDisc support.
Is someone working on DLL separation for ntdll.dll / kernel32.dll ? It seems that there's a lot of work to be done.
Laurent Pinchart
Laurent Pinchart laurent.pinchart@skynet.be writes:
Is someone working on DLL separation for ntdll.dll / kernel32.dll ? It seems that there's a lot of work to be done.
Quite a lot yes. But I think it makes more sense to first separate properly all the dlls that depend on kernel/ntdll before attacking that.
Eric Pouech eric.pouech@wanadoo.fr writes:
don't you mean extern char _end[] instead ? there's nothing currently which ensures that pe_header is at the start of the code (it relies on the fact it's the first on gcc/ld link option, and that ld allocates slots for compilation units in the order they are expressed) => this may become a porting issue I'm not sure either that _end is completly portable?
using this technique (with _start, _etext, _edata, _bss_start, _end) we could also compute the "real" size of code, data, and bss which should be of some interest (even if data would be a bit more difficult, since we need to take care of the rsrc part)
If you really want to get everything right you have to write a linker script. This also allows making the PE header the first thing in the image. It's not very portable but it's easy to have a linker script for each platform. I played a bit with that back when we were trying to make the Visual C++ debugger work on builtin dlls, I may be able to find this code somewhere if someone is interested.