1/* Add an uninitialized data section to an executable. 2 Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 3 2006, 2007 Free Software Foundation, Inc. 4 5This file is part of GNU Emacs. 6 7GNU Emacs is free software; you can redistribute it and/or modify 8it under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 2, or (at your option) 10any later version. 11 12GNU Emacs is distributed in the hope that it will be useful, 13but WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15GNU General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with GNU Emacs; see the file COPYING. If not, write to 19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20Boston, MA 02110-1301, USA. 21 22 Andrew Innes <andrewi@harlequin.co.uk> 04-Jan-1999 23 based on code from unexw32.c 24*/ 25 26#include <stdlib.h> 27#include <stdio.h> 28#include <fcntl.h> 29#include <time.h> 30#ifdef __GNUC__ 31#define _ANONYMOUS_UNION 32#define _ANONYMOUS_STRUCT 33#endif 34#include <windows.h> 35 36/* Include relevant definitions from IMAGEHLP.H, which can be found 37 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */ 38 39PIMAGE_NT_HEADERS 40(__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress, 41 DWORD FileLength, 42 LPDWORD HeaderSum, 43 LPDWORD CheckSum); 44 45#undef min 46#undef max 47#define min(x, y) (((x) < (y)) ? (x) : (y)) 48#define max(x, y) (((x) > (y)) ? (x) : (y)) 49 50 51/* File handling. */ 52 53typedef struct file_data { 54 char *name; 55 unsigned long size; 56 HANDLE file; 57 HANDLE file_mapping; 58 unsigned char *file_base; 59} file_data; 60 61int 62open_input_file (file_data *p_file, char *filename) 63{ 64 HANDLE file; 65 HANDLE file_mapping; 66 void *file_base; 67 unsigned long size, upper_size; 68 69 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, 70 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 71 if (file == INVALID_HANDLE_VALUE) 72 return FALSE; 73 74 size = GetFileSize (file, &upper_size); 75 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY, 76 0, size, NULL); 77 if (!file_mapping) 78 return FALSE; 79 80 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size); 81 if (file_base == 0) 82 return FALSE; 83 84 p_file->name = filename; 85 p_file->size = size; 86 p_file->file = file; 87 p_file->file_mapping = file_mapping; 88 p_file->file_base = file_base; 89 90 return TRUE; 91} 92 93int 94open_output_file (file_data *p_file, char *filename, unsigned long size) 95{ 96 HANDLE file; 97 HANDLE file_mapping; 98 void *file_base; 99 100 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, 101 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 102 if (file == INVALID_HANDLE_VALUE) 103 return FALSE; 104 105 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE, 106 0, size, NULL); 107 if (!file_mapping) 108 return FALSE; 109 110 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size); 111 if (file_base == 0) 112 return FALSE; 113 114 p_file->name = filename; 115 p_file->size = size; 116 p_file->file = file; 117 p_file->file_mapping = file_mapping; 118 p_file->file_base = file_base; 119 120 return TRUE; 121} 122 123/* Close the system structures associated with the given file. */ 124void 125close_file_data (file_data *p_file) 126{ 127 UnmapViewOfFile (p_file->file_base); 128 CloseHandle (p_file->file_mapping); 129 /* For the case of output files, set final size. */ 130 SetFilePointer (p_file->file, p_file->size, NULL, FILE_BEGIN); 131 SetEndOfFile (p_file->file); 132 CloseHandle (p_file->file); 133} 134 135 136/* Routines to manipulate NT executable file sections. */ 137 138unsigned long 139get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section) 140{ 141 /* The true section size, before rounding, for an initialized data or 142 code section. (Supposedly some linkers swap the meaning of these 143 two values.) */ 144 return min (p_section->SizeOfRawData, 145 p_section->Misc.VirtualSize); 146} 147 148/* Return pointer to section header for named section. */ 149IMAGE_SECTION_HEADER * 150find_section (char * name, IMAGE_NT_HEADERS * nt_header) 151{ 152 PIMAGE_SECTION_HEADER section; 153 int i; 154 155 section = IMAGE_FIRST_SECTION (nt_header); 156 157 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 158 { 159 if (strcmp (section->Name, name) == 0) 160 return section; 161 section++; 162 } 163 return NULL; 164} 165 166/* Return pointer to section header for section containing the given 167 relative virtual address. */ 168IMAGE_SECTION_HEADER * 169rva_to_section (DWORD rva, IMAGE_NT_HEADERS * nt_header) 170{ 171 PIMAGE_SECTION_HEADER section; 172 int i; 173 174 section = IMAGE_FIRST_SECTION (nt_header); 175 176 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 177 { 178 /* Some linkers (eg. the NT SDK linker I believe) swapped the 179 meaning of these two values - or rather, they ignored 180 VirtualSize entirely and always set it to zero. This affects 181 some very old exes (eg. gzip dated Dec 1993). Since 182 w32_executable_type relies on this function to work reliably, 183 we need to cope with this. */ 184 DWORD real_size = max (section->SizeOfRawData, 185 section->Misc.VirtualSize); 186 if (rva >= section->VirtualAddress 187 && rva < section->VirtualAddress + real_size) 188 return section; 189 section++; 190 } 191 return NULL; 192} 193 194/* Return pointer to section header for section containing the given 195 offset in its raw data area. */ 196IMAGE_SECTION_HEADER * 197offset_to_section (DWORD offset, IMAGE_NT_HEADERS * nt_header) 198{ 199 PIMAGE_SECTION_HEADER section; 200 int i; 201 202 section = IMAGE_FIRST_SECTION (nt_header); 203 204 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 205 { 206 if (offset >= section->PointerToRawData 207 && offset < section->PointerToRawData + section->SizeOfRawData) 208 return section; 209 section++; 210 } 211 return NULL; 212} 213 214/* Return offset to an object in dst, given offset in src. We assume 215 there is at least one section in both src and dst images, and that 216 the some sections may have been added to dst (after sections in src). */ 217static DWORD 218relocate_offset (DWORD offset, 219 IMAGE_NT_HEADERS * src_nt_header, 220 IMAGE_NT_HEADERS * dst_nt_header) 221{ 222 PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header); 223 PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header); 224 int i = 0; 225 226 while (offset >= src_section->PointerToRawData) 227 { 228 if (offset < src_section->PointerToRawData + src_section->SizeOfRawData) 229 break; 230 i++; 231 if (i == src_nt_header->FileHeader.NumberOfSections) 232 { 233 /* Handle offsets after the last section. */ 234 dst_section = IMAGE_FIRST_SECTION (dst_nt_header); 235 dst_section += dst_nt_header->FileHeader.NumberOfSections - 1; 236 while (dst_section->PointerToRawData == 0) 237 dst_section--; 238 while (src_section->PointerToRawData == 0) 239 src_section--; 240 return offset 241 + (dst_section->PointerToRawData + dst_section->SizeOfRawData) 242 - (src_section->PointerToRawData + src_section->SizeOfRawData); 243 } 244 src_section++; 245 dst_section++; 246 } 247 return offset + 248 (dst_section->PointerToRawData - src_section->PointerToRawData); 249} 250 251#define OFFSET_TO_RVA(offset, section) \ 252 (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData)) 253 254#define RVA_TO_OFFSET(rva, section) \ 255 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress)) 256 257#define RVA_TO_SECTION_OFFSET(rva, section) \ 258 ((DWORD)(rva) - section->VirtualAddress) 259 260/* Convert address in executing image to RVA. */ 261#define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL)) 262 263#define PTR_TO_OFFSET(ptr, pfile_data) \ 264 ((unsigned char *)(ptr) - (pfile_data)->file_base) 265 266#define OFFSET_TO_PTR(offset, pfile_data) \ 267 ((pfile_data)->file_base + (DWORD)(offset)) 268 269#define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1)) 270#define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1)) 271 272 273static void 274copy_executable_and_add_section (file_data *p_infile, 275 file_data *p_outfile, 276 char *new_section_name, 277 DWORD new_section_size) 278{ 279 unsigned char *dst; 280 PIMAGE_DOS_HEADER dos_header; 281 PIMAGE_NT_HEADERS nt_header; 282 PIMAGE_NT_HEADERS dst_nt_header; 283 PIMAGE_SECTION_HEADER section; 284 PIMAGE_SECTION_HEADER dst_section; 285 DWORD offset; 286 int i; 287 int be_verbose = GetEnvironmentVariable ("DEBUG_DUMP", NULL, 0) > 0; 288 289#define COPY_CHUNK(message, src, size, verbose) \ 290 do { \ 291 unsigned char *s = (void *)(src); \ 292 unsigned long count = (size); \ 293 if (verbose) \ 294 { \ 295 printf ("%s\n", (message)); \ 296 printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); \ 297 printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \ 298 printf ("\t0x%08x Size in bytes.\n", count); \ 299 } \ 300 memcpy (dst, s, count); \ 301 dst += count; \ 302 } while (0) 303 304#define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile) 305#define ROUND_UP_DST_AND_ZERO(align) \ 306 do { \ 307 unsigned char *newdst = p_outfile->file_base \ 308 + ROUND_UP (DST_TO_OFFSET (), (align)); \ 309 /* Zero the alignment slop; it may actually initialize real data. */ \ 310 memset (dst, 0, newdst - dst); \ 311 dst = newdst; \ 312 } while (0) 313 314 /* Copy the source image sequentially, ie. section by section after 315 copying the headers and section table, to simplify the process of 316 adding an extra section table entry (which might force the raw 317 section data to be relocated). 318 319 Note that dst is updated implicitly by each COPY_CHUNK. */ 320 321 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base; 322 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) + 323 dos_header->e_lfanew); 324 section = IMAGE_FIRST_SECTION (nt_header); 325 326 dst = (unsigned char *) p_outfile->file_base; 327 328 COPY_CHUNK ("Copying DOS header...", dos_header, 329 (DWORD) nt_header - (DWORD) dos_header, be_verbose); 330 dst_nt_header = (PIMAGE_NT_HEADERS) dst; 331 COPY_CHUNK ("Copying NT header...", nt_header, 332 (DWORD) section - (DWORD) nt_header, be_verbose); 333 dst_section = (PIMAGE_SECTION_HEADER) dst; 334 COPY_CHUNK ("Copying section table...", section, 335 nt_header->FileHeader.NumberOfSections * sizeof (*section), 336 be_verbose); 337 338 /* To improve the efficiency of demand loading, make the file 339 alignment match the section alignment (VC++ 6.0 does this by 340 default anyway). */ 341 dst_nt_header->OptionalHeader.FileAlignment = 342 dst_nt_header->OptionalHeader.SectionAlignment; 343 344 /* Add an uninitialized data section at the end, of the specified name 345 and virtual size. */ 346 if (find_section (new_section_name, nt_header) == NULL) 347 /* Leave room for extra section table entry; filled in below. */ 348 dst += sizeof (*section); 349 else 350 new_section_name = NULL; 351 352 /* Align the first section's raw data area, and set the header size 353 field accordingly. */ 354 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment); 355 dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET (); 356 357 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 358 { 359 char msg[100]; 360 /* Windows section names are fixed 8-char strings, only 361 zero-terminated if the name is shorter than 8 characters. */ 362 sprintf (msg, "Copying raw data for %.8s...", section->Name); 363 364 /* Update the file-relative offset for this section's raw data (if 365 it has any) in case things have been relocated; we will update 366 the other offsets below once we know where everything is. */ 367 if (dst_section->PointerToRawData) 368 dst_section->PointerToRawData = DST_TO_OFFSET (); 369 370 /* Can always copy the original raw data. */ 371 COPY_CHUNK 372 (msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile), 373 section->SizeOfRawData, be_verbose); 374 375 /* Round up the raw data size to the new alignment. */ 376 dst_section->SizeOfRawData = 377 ROUND_UP (dst_section->SizeOfRawData, 378 dst_nt_header->OptionalHeader.FileAlignment); 379 380 /* Align the next section's raw data area. */ 381 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment); 382 383 section++; 384 dst_section++; 385 } 386 387 /* Add the extra section entry (which adds no raw data). */ 388 if (new_section_name != NULL) 389 { 390 dst_nt_header->FileHeader.NumberOfSections++; 391 dst_nt_header->OptionalHeader.SizeOfImage += new_section_size; 392 strncpy (dst_section->Name, new_section_name, sizeof (dst_section->Name)); 393 dst_section->VirtualAddress = 394 section[-1].VirtualAddress 395 + ROUND_UP (section[-1].Misc.VirtualSize, 396 dst_nt_header->OptionalHeader.SectionAlignment); 397 dst_section->Misc.VirtualSize = new_section_size; 398 dst_section->PointerToRawData = 0; 399 dst_section->SizeOfRawData = 0; 400 dst_section->Characteristics = 401 IMAGE_SCN_CNT_UNINITIALIZED_DATA 402 | IMAGE_SCN_MEM_READ 403 | IMAGE_SCN_MEM_WRITE; 404 } 405 406 /* Copy remainder of source image. */ 407 section--; 408 offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData, 409 nt_header->OptionalHeader.FileAlignment); 410 COPY_CHUNK 411 ("Copying remainder of executable...", 412 OFFSET_TO_PTR (offset, p_infile), 413 p_infile->size - offset, be_verbose); 414 415 /* Final size for new image. */ 416 p_outfile->size = DST_TO_OFFSET (); 417 418 /* Now patch up remaining file-relative offsets. */ 419 section = IMAGE_FIRST_SECTION (nt_header); 420 dst_section = IMAGE_FIRST_SECTION (dst_nt_header); 421 422#define ADJUST_OFFSET(var) \ 423 do { \ 424 if ((var) != 0) \ 425 (var) = relocate_offset ((var), nt_header, dst_nt_header); \ 426 } while (0) 427 428 dst_nt_header->OptionalHeader.SizeOfInitializedData = 0; 429 dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0; 430 for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++) 431 { 432 /* Recompute data sizes for completeness. */ 433 if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) 434 dst_nt_header->OptionalHeader.SizeOfInitializedData += 435 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment); 436 else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) 437 dst_nt_header->OptionalHeader.SizeOfUninitializedData += 438 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment); 439 440 ADJUST_OFFSET (dst_section[i].PointerToLinenumbers); 441 } 442 443 ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable); 444 445 /* Update offsets in debug directory entries. */ 446 { 447 IMAGE_DATA_DIRECTORY debug_dir = 448 dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; 449 PIMAGE_DEBUG_DIRECTORY debug_entry; 450 451 section = rva_to_section (debug_dir.VirtualAddress, dst_nt_header); 452 if (section) 453 { 454 debug_entry = (PIMAGE_DEBUG_DIRECTORY) 455 (RVA_TO_OFFSET (debug_dir.VirtualAddress, section) + p_outfile->file_base); 456 debug_dir.Size /= sizeof (IMAGE_DEBUG_DIRECTORY); 457 458 for (i = 0; i < debug_dir.Size; i++, debug_entry++) 459 ADJUST_OFFSET (debug_entry->PointerToRawData); 460 } 461 } 462} 463 464 465int 466main (int argc, char **argv) 467{ 468 file_data in_file, out_file; 469 char out_filename[MAX_PATH], in_filename[MAX_PATH]; 470 unsigned long size; 471 PIMAGE_DOS_HEADER dos_header; 472 PIMAGE_NT_HEADERS nt_header; 473 474#define OLD_NAME argv[1] 475#define NEW_NAME argv[2] 476#define SECTION_NAME argv[3] 477#define SECTION_SIZE argv[4] 478 479 strcpy (in_filename, OLD_NAME); 480 strcpy (out_filename, NEW_NAME); 481 482 printf ("Dumping from %s\n", in_filename); 483 printf (" to %s\n", out_filename); 484 485 /* Open the undumped executable file. */ 486 if (!open_input_file (&in_file, in_filename)) 487 { 488 printf ("Failed to open %s (%d)...bailing.\n", 489 in_filename, GetLastError ()); 490 exit (1); 491 } 492 dos_header = (PIMAGE_DOS_HEADER) in_file.file_base; 493 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew); 494 /* Allow for expansion due to increasing file align to section align. 495 We can overestimate here, since close_file_data will update the 496 size exactly. */ 497 size = in_file.size 498 + nt_header->OptionalHeader.SectionAlignment 499 * nt_header->FileHeader.NumberOfSections; 500 if (!open_output_file (&out_file, out_filename, size)) 501 { 502 printf ("Failed to open %s (%d)...bailing.\n", 503 out_filename, GetLastError ()); 504 exit (1); 505 } 506 507 copy_executable_and_add_section (&in_file, &out_file, 508 SECTION_NAME, 509 atoi (SECTION_SIZE) * 1024 * 1024); 510 511 /* Patch up header fields; profiler is picky about this. */ 512 { 513 HANDLE hImagehelp = LoadLibrary ("imagehlp.dll"); 514 DWORD headersum; 515 DWORD checksum; 516 517 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base; 518 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew); 519 520 nt_header->OptionalHeader.CheckSum = 0; 521// nt_header->FileHeader.TimeDateStamp = time (NULL); 522// dos_header->e_cp = size / 512; 523// nt_header->OptionalHeader.SizeOfImage = size; 524 525 pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile"); 526 if (pfnCheckSumMappedFile) 527 { 528// nt_header->FileHeader.TimeDateStamp = time (NULL); 529 pfnCheckSumMappedFile (out_file.file_base, 530 out_file.size, 531 &headersum, 532 &checksum); 533 nt_header->OptionalHeader.CheckSum = checksum; 534 } 535 FreeLibrary (hImagehelp); 536 } 537 538 close_file_data (&in_file); 539 close_file_data (&out_file); 540 541 return 0; 542} 543 544/* eof */ 545 546/* arch-tag: 17e2b0aa-8c17-4bd1-b24b-1cda689245fa 547 (do not change this comment) */ 548