This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[qboosh@pld-linux.org: binutils 2.14.90.0.8 - ld crash when cross-linking PE DLL on 64-bit archs]


Can someone take a look at it?


H.J.
--- Begin Message ---
Hello,

When trying to make cross mingw32 environment on 64-bit archs
(alpha-linux, amd64-linux), ld (binutils 2.14.90.0.8 tested) catches
SEGV on the following command:

| ld --dll -Bdynamic -e _DllMainCRTStartup@12 -o mingwthrd_dummy.exe
| ./dllcrt2.o -Lmingwex -L. -L/usr/lib/gcc-lib/i386-mingw32/3.3.2
| L/usr/lib/gcc-lib/i386-mingw32/3.3.2/../../../../i386-mingw32/lib
| --base-file=mingwthrd.base mingwthrd.exp --image-base 0x6FBC0000 --entry
| _DllMainCRTStartup@12 mthr.o mthr_init.o -lmingw32 -lgcc -lmoldname
| -lmingwex -lmsvcrt -luser32 -lkernel32 -ladvapi32 -lshell32 -lmingw32
| -lgcc -lmoldname -lmingwex -lmsvcrt

SEGV appears inside malloc(), after corruption of some malloc structs.
Using ElectricFence I found that ld writes past allocated area in
fill_edata() function (ld/pe-dll.c).

edata_d is pointer to allocated edata_sz bytes, where edata_sz is
calculated by:

|   /* OK, now we can allocate some memory.  */
|   edata_sz = (40                                /* directory */
|               + 4 * export_table_size           /* addresses */
|               + 4 * count_exported_byname       /* name ptrs */
|               + 2 * count_exported_byname       /* ordinals */
|               + name_table_size + strlen (dll_name) + 1);

but then pointers to edata areas are calculated inconsequently:

|   unsigned char *edirectory;
|   unsigned long *eaddresses;
|   unsigned long *enameptrs;
|   unsigned short *eordinals;
|   unsigned char *enamestr;
[...]
|   edata_d = xmalloc (edata_sz);
| 
|   /* Note use of array pointer math here.  */
|   edirectory = edata_d;
|   eaddresses = (unsigned long *) (edata_d + 40);
|   enameptrs = eaddresses + export_table_size;
|   eordinals = (unsigned short *) (enameptrs + count_exported_byname);
|   enamestr = (char *) (eordinals + count_exported_byname);

These pointers are wrong if sizeof(unsigned long)!=4 or
sizeof(unsigned short)!=2.
On alpha-linux and amd64-linux it's the first case (long is 8-byte long).

I'm attaching the patch I've used - it changes unsinged long to uint32_t
in above pointers. It works for me on alpha-linux and amd64-linux, doesn't
break anything on x86-linux and sparc-linux.
I don't know what is binutils policy on <stdint.h> - is using uint32_t
OK, or binutils has some other name for exactly 32-bit long type.
unsigned shorts above probably should be changed to uint16_t (or
equivalent), but I didn't touch it as all archs I'm using have 16-bit
unsigned short.


-- 
Jakub Bogusz    http://cyber.cs.net.pl/~qboosh/
PLD Team        http://www.pld-linux.org/

--- binutils-2.14.90.0.8/ld/pe-dll.c.orig	2004-01-14 21:07:52.000000000 +0000
+++ binutils-2.14.90.0.8/ld/pe-dll.c	2004-02-18 21:56:38.000000000 +0000
@@ -25,6 +25,7 @@
 #include "libiberty.h"
 #include "safe-ctype.h"
 
+#include <stdint.h>
 #include <time.h>
 
 #include "ld.h"
@@ -916,8 +917,8 @@
 {
   int s, hint;
   unsigned char *edirectory;
-  unsigned long *eaddresses;
-  unsigned long *enameptrs;
+  uint32_t *eaddresses;
+  uint32_t *enameptrs;
   unsigned short *eordinals;
   unsigned char *enamestr;
   time_t now;
@@ -928,7 +929,7 @@
 
   /* Note use of array pointer math here.  */
   edirectory = edata_d;
-  eaddresses = (unsigned long *) (edata_d + 40);
+  eaddresses = (uint32_t *) (edata_d + 40);
   enameptrs = eaddresses + export_table_size;
   eordinals = (unsigned short *) (enameptrs + count_exported_byname);
   enamestr = (char *) (eordinals + count_exported_byname);


--- End Message ---

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]