/* * Usage: rsym input-file output-file * * There are two sources of information: the .stab/.stabstr * sections of the executable and the COFF symbol table. Most * of the information is in the .stab/.stabstr sections. * However, most of our asm files don't contain .stab directives, * so routines implemented in assembler won't show up in the * .stab section. They are present in the COFF symbol table. * So, we mostly use the .stab/.stabstr sections, but we augment * the info there with info from the COFF symbol table when * possible. * * This is a tool and is compiled using the host compiler, * i.e. on Linux gcc and not mingw-gcc (cross-compiler). * Therefore we can't include SDK headers and we have to * duplicate some definitions here. * Also note that the internal functions are "old C-style", * returning an int, where a return of 0 means success and * non-zero is failure. */ #include "../../dll/win32/dbghelp/compat.h" #include #include #include #include #include #include "rsym.h" #define MAX_PATH 260 #define MAX_SYM_NAME 2000 struct StringEntry { struct StringEntry *Next; ULONG Offset; char *String; }; struct StringHashTable { ULONG TableSize; struct StringEntry **Table; }; /* This is the famous DJB hash */ static unsigned int ComputeDJBHash(const char *name) { unsigned int val = 5381; int i = 0; for (i = 0; name[i]; i++) { val = (33 * val) + name[i]; } return val; } static void AddStringToHash(struct StringHashTable *StringTable, unsigned int hash, ULONG Offset, char *StringPtr) { struct StringEntry *entry = calloc(1, sizeof(struct StringEntry)); entry->Offset = Offset; entry->String = StringPtr; entry->Next = StringTable->Table[hash]; StringTable->Table[hash] = entry; } static void StringHashTableInit(struct StringHashTable *StringTable, ULONG StringsLength, char *StringsBase) { char *Start = StringsBase; char *End = StringsBase + StringsLength; StringTable->TableSize = 1024; StringTable->Table = calloc(1024, sizeof(struct StringEntry *)); while (Start < End) { AddStringToHash(StringTable, ComputeDJBHash(Start) % StringTable->TableSize, Start - StringsBase, Start); Start += strlen(Start) + 1; } } static void StringHashTableFree(struct StringHashTable *StringTable) { int i; struct StringEntry *entry; for (i = 0; i < StringTable->TableSize; i++) { while ((entry = StringTable->Table[i])) { entry = entry->Next; free(StringTable->Table[i]); StringTable->Table[i] = entry; } } free(StringTable->Table); } static int CompareSymEntry(const PROSSYM_ENTRY SymEntry1, const PROSSYM_ENTRY SymEntry2) { if (SymEntry1->Address < SymEntry2->Address) { return -1; } if (SymEntry2->Address < SymEntry1->Address) { return +1; } if (SymEntry2->SourceLine == 0) { return -1; } if (SymEntry1->SourceLine == 0) { return +1; } return 0; } static int GetStabInfo(void *FileData, PIMAGE_FILE_HEADER PEFileHeader, PIMAGE_SECTION_HEADER PESectionHeaders, ULONG *StabSymbolsLength, void **StabSymbolsBase, ULONG *StabStringsLength, void **StabStringsBase) { ULONG Idx; /* Load .stab and .stabstr sections if available */ *StabSymbolsBase = NULL; *StabSymbolsLength = 0; *StabStringsBase = NULL; *StabStringsLength = 0; for (Idx = 0; Idx < PEFileHeader->NumberOfSections; Idx++) { /* printf("section: '%.08s'\n", PESectionHeaders[Idx].Name); */ if ((strncmp((char *) PESectionHeaders[Idx].Name, ".stab", 5) == 0) && (PESectionHeaders[Idx].Name[5] == 0)) { /* printf(".stab section found. Size %d\n", PESectionHeaders[Idx].SizeOfRawData); */ *StabSymbolsLength = PESectionHeaders[Idx].SizeOfRawData; *StabSymbolsBase = (void *)((char *) FileData + PESectionHeaders[Idx].PointerToRawData); } if (strncmp((char *) PESectionHeaders[Idx].Name, ".stabstr", 8) == 0) { /* printf(".stabstr section found. Size %d\n", PESectionHeaders[Idx].SizeOfRawData); */ *StabStringsLength = PESectionHeaders[Idx].SizeOfRawData; *StabStringsBase = (void *)((char *) FileData + PESectionHeaders[Idx].PointerToRawData); } } return 0; } static int GetCoffInfo(void *FileData, PIMAGE_FILE_HEADER PEFileHeader, PIMAGE_SECTION_HEADER PESectionHeaders, ULONG *CoffSymbolsLength, void **CoffSymbolsBase, ULONG *CoffStringsLength, void **CoffStringsBase) { if (PEFileHeader->PointerToSymbolTable == 0 || PEFileHeader->NumberOfSymbols == 0) { /* No COFF symbol table */ *CoffSymbolsLength = 0; *CoffStringsLength = 0; } else { *CoffSymbolsLength = PEFileHeader->NumberOfSymbols * sizeof(COFF_SYMENT); *CoffSymbolsBase = (void *)((char *) FileData + PEFileHeader->PointerToSymbolTable); *CoffStringsLength = *((ULONG *) ((char *) *CoffSymbolsBase + *CoffSymbolsLength)); *CoffStringsBase = (void *)((char *) *CoffSymbolsBase + *CoffSymbolsLength); } return 0; } static ULONG FindOrAddString(struct StringHashTable *StringTable, char *StringToFind, ULONG *StringsLength, void *StringsBase) { unsigned int hash = ComputeDJBHash(StringToFind) % StringTable->TableSize; struct StringEntry *entry = StringTable->Table[hash]; while (entry && strcmp(entry->String, StringToFind)) entry = entry->Next; if (entry) { return entry->Offset; } else { char *End = (char *)StringsBase + *StringsLength; strcpy(End, StringToFind); *StringsLength += strlen(StringToFind) + 1; AddStringToHash(StringTable, hash, End - (char *)StringsBase, End); return End - (char *)StringsBase; } } static int ConvertStabs(ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase, ULONG *StringsLength, void *StringsBase, ULONG StabSymbolsLength, void *StabSymbolsBase, ULONG StabStringsLength, void *StabStringsBase, ULONG_PTR ImageBase, PIMAGE_FILE_HEADER PEFileHeader, PIMAGE_SECTION_HEADER PESectionHeaders) { PSTAB_ENTRY StabEntry; ULONG Count, i; ULONG_PTR Address, LastFunctionAddress; int First = 1; char *Name; ULONG NameLen; char FuncName[256]; PROSSYM_ENTRY Current; struct StringHashTable StringHash; StabEntry = StabSymbolsBase; Count = StabSymbolsLength / sizeof(STAB_ENTRY); *SymbolsCount = 0; if (Count == 0) { /* No symbol info */ *SymbolsBase = NULL; return 0; } *SymbolsBase = malloc(Count * sizeof(ROSSYM_ENTRY)); if (*SymbolsBase == NULL) { fprintf(stderr, "Failed to allocate memory for converted .stab symbols\n"); return 1; } Current = *SymbolsBase; memset(Current, 0, sizeof(*Current)); StringHashTableInit(&StringHash, *StringsLength, (char *)StringsBase); LastFunctionAddress = 0; for (i = 0; i < Count; i++) { if (LastFunctionAddress == 0) { Address = StabEntry[i].n_value - ImageBase; } else { Address = LastFunctionAddress + StabEntry[i].n_value; } switch (StabEntry[i].n_type) { case N_SO: case N_SOL: case N_BINCL: Name = (char *) StabStringsBase + StabEntry[i].n_strx; if (StabStringsLength < StabEntry[i].n_strx || *Name == '\0' || Name[strlen(Name) - 1] == '/' || Name[strlen(Name) - 1] == '\\' || StabEntry[i].n_value < ImageBase) { continue; } if (First || Address != Current->Address) { if (!First) { memset(++Current, 0, sizeof(*Current)); Current->FunctionOffset = Current[-1].FunctionOffset; } else First = 0; Current->Address = Address; } Current->FileOffset = FindOrAddString(&StringHash, (char *)StabStringsBase + StabEntry[i].n_strx, StringsLength, StringsBase); break; case N_FUN: if (StabEntry[i].n_desc == 0 || StabEntry[i].n_value < ImageBase) { LastFunctionAddress = 0; /* line # 0 = end of function */ continue; } if (First || Address != Current->Address) { if (!First) memset(++Current, 0, sizeof(*Current)); else First = 0; Current->Address = Address; Current->FileOffset = Current[-1].FileOffset; } Name = (char *)StabStringsBase + StabEntry[i].n_strx; NameLen = strcspn(Name, ":"); if (sizeof(FuncName) <= NameLen) { free(*SymbolsBase); fprintf(stderr, "Function name too long\n"); return 1; } memcpy(FuncName, Name, NameLen); FuncName[NameLen] = '\0'; Current->FunctionOffset = FindOrAddString(&StringHash, FuncName, StringsLength, StringsBase); Current->SourceLine = 0; LastFunctionAddress = Address; break; case N_SLINE: if (First || Address != Current->Address) { if (!First) { memset(++Current, 0, sizeof(*Current)); Current->FileOffset = Current[-1].FileOffset; Current->FunctionOffset = Current[-1].FunctionOffset; } else First = 0; Current->Address = Address; } Current->SourceLine = StabEntry[i].n_desc; break; default: continue; } } *SymbolsCount = (Current - *SymbolsBase + 1); qsort(*SymbolsBase, *SymbolsCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *)) CompareSymEntry); StringHashTableFree(&StringHash); return 0; } static int ConvertCoffs(ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase, ULONG *StringsLength, void *StringsBase, ULONG CoffSymbolsLength, void *CoffSymbolsBase, ULONG CoffStringsLength, void *CoffStringsBase, ULONG_PTR ImageBase, PIMAGE_FILE_HEADER PEFileHeader, PIMAGE_SECTION_HEADER PESectionHeaders) { ULONG Count, i; PCOFF_SYMENT CoffEntry; char FuncName[256], FileName[1024]; char *p; PROSSYM_ENTRY Current; struct StringHashTable StringHash; CoffEntry = (PCOFF_SYMENT) CoffSymbolsBase; Count = CoffSymbolsLength / sizeof(COFF_SYMENT); *SymbolsBase = malloc(Count * sizeof(ROSSYM_ENTRY)); if (*SymbolsBase == NULL) { fprintf(stderr, "Unable to allocate memory for converted COFF symbols\n"); return 1; } *SymbolsCount = 0; Current = *SymbolsBase; StringHashTableInit(&StringHash, *StringsLength, (char*)StringsBase); for (i = 0; i < Count; i++) { if (ISFCN(CoffEntry[i].e_type) || C_EXT == CoffEntry[i].e_sclass) { Current->Address = CoffEntry[i].e_value; if (CoffEntry[i].e_scnum > 0) { if (PEFileHeader->NumberOfSections < CoffEntry[i].e_scnum) { free(*SymbolsBase); fprintf(stderr, "Invalid section number %d in COFF symbols (only %d sections present)\n", CoffEntry[i].e_scnum, PEFileHeader->NumberOfSections); return 1; } Current->Address += PESectionHeaders[CoffEntry[i].e_scnum - 1].VirtualAddress; } Current->FileOffset = 0; if (CoffEntry[i].e.e.e_zeroes == 0) { if (sizeof(FuncName) <= strlen((char *) CoffStringsBase + CoffEntry[i].e.e.e_offset)) { free(*SymbolsBase); fprintf(stderr, "Function name too long\n"); StringHashTableFree(&StringHash); return 1; } strcpy(FuncName, (char *) CoffStringsBase + CoffEntry[i].e.e.e_offset); } else { memcpy(FuncName, CoffEntry[i].e.e_name, E_SYMNMLEN); FuncName[E_SYMNMLEN] = '\0'; } /* Name demangling: stdcall */ p = strrchr(FuncName, '@'); if (p != NULL) { *p = '\0'; } p = ('_' == FuncName[0] || '@' == FuncName[0] ? FuncName + 1 : FuncName); Current->FunctionOffset = FindOrAddString(&StringHash, p, StringsLength, StringsBase); Current->SourceLine = 0; memset(++Current, 0, sizeof(*Current)); } i += CoffEntry[i].e_numaux; } *SymbolsCount = (Current - *SymbolsBase + 1); qsort(*SymbolsBase, *SymbolsCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *)) CompareSymEntry); StringHashTableFree(&StringHash); return 0; } struct DbgHelpLineEntry { ULONG vma; ULONG fileId; ULONG functionId; ULONG line; }; struct DbgHelpStringTab { ULONG Length; ULONG Bytes; char ***Table; ULONG LineEntries, CurLineEntries; struct DbgHelpLineEntry *LineEntryData; void *process; DWORD module_base; char *PathChop; char *SourcePath; struct DbgHelpLineEntry *lastLineEntry; }; static struct DbgHelpLineEntry* DbgHelpAddLineEntry(struct DbgHelpStringTab *tab) { if (tab->CurLineEntries == tab->LineEntries) { struct DbgHelpLineEntry *newEntries = realloc(tab->LineEntryData, tab->LineEntries * 2 * sizeof(struct DbgHelpLineEntry)); if (!newEntries) return 0; tab->LineEntryData = newEntries; memset(tab->LineEntryData + tab->LineEntries, 0, sizeof(struct DbgHelpLineEntry) * tab->LineEntries); tab->LineEntries *= 2; } return &tab->LineEntryData[tab->CurLineEntries++]; } static int DbgHelpAddStringToTable(struct DbgHelpStringTab *tab, char *name) { unsigned int bucket = ComputeDJBHash(name) % tab->Length; char **tabEnt = tab->Table[bucket]; int i; char **newBucket; if (tabEnt) { for (i = 0; tabEnt[i] && strcmp(tabEnt[i], name); i++); if (tabEnt[i]) { free(name); return (i << 10) | bucket; } } else i = 0; /* At this point, we need to insert */ tab->Bytes += strlen(name) + 1; newBucket = realloc(tab->Table[bucket], (i+2) * sizeof(char *)); if (!newBucket) { fprintf(stderr, "realloc failed!\n"); return -1; } tab->Table[bucket] = newBucket; tab->Table[bucket][i+1] = 0; tab->Table[bucket][i] = name; return (i << 10) | bucket; } const char* DbgHelpGetString(struct DbgHelpStringTab *tab, int id) { int i = id >> 10; int bucket = id & 0x3ff; return tab->Table[bucket][i]; } /* Remove a prefix of PathChop if it exists and return a copy of the tail. */ static char * StrDupShortenPath(char *PathChop, char *FilePath) { int pclen = strlen(PathChop); if (!strncmp(FilePath, PathChop, pclen)) { return strdup(FilePath+pclen); } else { return strdup(FilePath); } } static BOOL DbgHelpAddLineNumber(PSRCCODEINFO LineInfo, void *UserContext) { struct DbgHelpStringTab *tab = (struct DbgHelpStringTab *)UserContext; DWORD64 disp; int fileId, functionId; PSYMBOL_INFO pSymbol = malloc(FIELD_OFFSET(SYMBOL_INFO, Name[MAX_SYM_NAME])); if (!pSymbol) return FALSE; memset(pSymbol, 0, FIELD_OFFSET(SYMBOL_INFO, Name[MAX_SYM_NAME])); /* If any file can be opened by relative path up to a certain level, then record that path. */ if (!tab->PathChop) { int i, endLen; char *end = strrchr(LineInfo->FileName, '/'); if (!end) end = strrchr(LineInfo->FileName, '\\'); if (end) { for (i = (end - LineInfo->FileName) - 1; i >= 0; i--) { if (LineInfo->FileName[i] == '/' || LineInfo->FileName[i] == '\\') { char *synthname = malloc(strlen(tab->SourcePath) + strlen(LineInfo->FileName + i + 1) + 2); strcpy(synthname, tab->SourcePath); strcat(synthname, "/"); strcat(synthname, LineInfo->FileName + i + 1); FILE *f = fopen(synthname, "r"); free(synthname); if (f) { fclose(f); break; } } } i++; /* Be in the string or past the next slash */ tab->PathChop = malloc(i + 1); memcpy(tab->PathChop, LineInfo->FileName, i); tab->PathChop[i] = 0; } } fileId = DbgHelpAddStringToTable(tab, StrDupShortenPath(tab->PathChop, LineInfo->FileName)); pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; if (!SymFromAddr(tab->process, LineInfo->Address, &disp, pSymbol)) { //fprintf(stderr, "SymFromAddr failed.\n"); free(pSymbol); return FALSE; } functionId = DbgHelpAddStringToTable(tab, strdup(pSymbol->Name)); if (LineInfo->Address == 0) fprintf(stderr, "Address is 0.\n"); tab->lastLineEntry = DbgHelpAddLineEntry(tab); tab->lastLineEntry->vma = LineInfo->Address - LineInfo->ModBase; tab->lastLineEntry->functionId = functionId; tab->lastLineEntry->fileId = fileId; tab->lastLineEntry->line = LineInfo->LineNumber; free(pSymbol); return TRUE; } static int ConvertDbgHelp(void *process, DWORD module_base, char *SourcePath, ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase, ULONG *StringsLength, void **StringsBase) { char *strings, *strings_copy; int i, j, bucket, entry; PROSSYM_ENTRY rossym; struct DbgHelpStringTab strtab = { 0 }; strtab.process = process; strtab.module_base = module_base; strtab.Bytes = 1; strtab.Length = 1024; strtab.Table = calloc(1024, sizeof(const char **)); strtab.Table[0] = calloc(2, sizeof(const char *)); strtab.Table[0][0] = strdup(""); // The zero string strtab.CurLineEntries = 0; strtab.LineEntries = 16384; strtab.LineEntryData = calloc(strtab.LineEntries, sizeof(struct DbgHelpLineEntry)); strtab.PathChop = NULL; strtab.SourcePath = SourcePath ? SourcePath : ""; SymEnumLines(process, module_base, NULL, NULL, DbgHelpAddLineNumber, &strtab); /* Transcribe necessary strings */ *StringsLength = strtab.Bytes; strings = strings_copy = ((char *)(*StringsBase = malloc(strtab.Bytes))); /* Copy in strings */ for (i = 0; i < strtab.Length; i++) { for (j = 0; strtab.Table[i] && strtab.Table[i][j]; j++) { /* Each entry is replaced by its corresponding entry in our string section. We can substract the strings origin to get an offset. */ char *toFree = strtab.Table[i][j]; strtab.Table[i][j] = strcpy(strings_copy, strtab.Table[i][j]); free(toFree); strings_copy += strlen(strings_copy) + 1; } } assert(strings_copy == strings + strtab.Bytes); *SymbolsBase = calloc(strtab.CurLineEntries, sizeof(ROSSYM_ENTRY)); *SymbolsCount = strtab.CurLineEntries; /* Copy symbols into rossym entries */ for (i = 0; i < strtab.CurLineEntries; i++) { rossym = &(*SymbolsBase)[i]; rossym->Address = strtab.LineEntryData[i].vma; bucket = strtab.LineEntryData[i].fileId & 0x3ff; entry = strtab.LineEntryData[i].fileId >> 10; rossym->FileOffset = strtab.Table[bucket][entry] - strings; bucket = strtab.LineEntryData[i].functionId & 0x3ff; entry = strtab.LineEntryData[i].functionId >> 10; rossym->FunctionOffset = strtab.Table[bucket][entry] - strings; rossym->SourceLine = strtab.LineEntryData[i].line; } /* Free stringtab */ for (i = 0; i < strtab.Length; i++) { free(strtab.Table[i]); } free(strtab.LineEntryData); free(strtab.PathChop); qsort(*SymbolsBase, *SymbolsCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *))CompareSymEntry); return 0; } static int MergeStabsAndCoffs(ULONG *MergedSymbolCount, PROSSYM_ENTRY *MergedSymbols, ULONG StabSymbolsCount, PROSSYM_ENTRY StabSymbols, ULONG CoffSymbolsCount, PROSSYM_ENTRY CoffSymbols) { ULONG StabIndex, j; ULONG CoffIndex; ULONG_PTR StabFunctionStartAddress; ULONG StabFunctionStringOffset, NewStabFunctionStringOffset, CoffFunctionStringOffset; PROSSYM_ENTRY CoffFunctionSymbol; *MergedSymbolCount = 0; if (StabSymbolsCount == 0) { *MergedSymbols = NULL; return 0; } *MergedSymbols = malloc((StabSymbolsCount + CoffSymbolsCount) * sizeof(ROSSYM_ENTRY)); if (*MergedSymbols == NULL) { fprintf(stderr, "Unable to allocate memory for merged symbols\n"); return 1; } StabFunctionStartAddress = 0; StabFunctionStringOffset = 0; CoffFunctionStringOffset = 0; CoffFunctionSymbol = NULL; CoffIndex = 0; for (StabIndex = 0; StabIndex < StabSymbolsCount; StabIndex++) { (*MergedSymbols)[*MergedSymbolCount] = StabSymbols[StabIndex]; for (j = StabIndex + 1; j < StabSymbolsCount && StabSymbols[j].Address == StabSymbols[StabIndex].Address; j++) { if (StabSymbols[j].FileOffset != 0 && (*MergedSymbols)[*MergedSymbolCount].FileOffset == 0) { (*MergedSymbols)[*MergedSymbolCount].FileOffset = StabSymbols[j].FileOffset; } if (StabSymbols[j].FunctionOffset != 0 && (*MergedSymbols)[*MergedSymbolCount].FunctionOffset == 0) { (*MergedSymbols)[*MergedSymbolCount].FunctionOffset = StabSymbols[j].FunctionOffset; } if (StabSymbols[j].SourceLine != 0 && (*MergedSymbols)[*MergedSymbolCount].SourceLine == 0) { (*MergedSymbols)[*MergedSymbolCount].SourceLine = StabSymbols[j].SourceLine; } } StabIndex = j - 1; while (CoffIndex < CoffSymbolsCount && CoffSymbols[CoffIndex].Address <= (*MergedSymbols)[*MergedSymbolCount].Address) { if (CoffSymbols[CoffIndex].FunctionOffset != 0) { CoffFunctionSymbol = &CoffSymbols[CoffIndex]; CoffFunctionStringOffset = CoffFunctionSymbol->FunctionOffset; } CoffIndex++; } NewStabFunctionStringOffset = (*MergedSymbols)[*MergedSymbolCount].FunctionOffset; if (CoffFunctionSymbol && CoffFunctionSymbol->Address <= (*MergedSymbols)[*MergedSymbolCount].Address && StabFunctionStartAddress < CoffFunctionSymbol->Address) { (*MergedSymbols)[*MergedSymbolCount].FunctionOffset = CoffFunctionStringOffset; CoffFunctionSymbol->FunctionOffset = 0; } if (StabFunctionStringOffset != NewStabFunctionStringOffset) { StabFunctionStartAddress = (*MergedSymbols)[*MergedSymbolCount].Address; } StabFunctionStringOffset = NewStabFunctionStringOffset; (*MergedSymbolCount)++; } /* Handle functions that have no analog in the upstream data */ for (CoffIndex = 0; CoffIndex < CoffSymbolsCount; CoffIndex++) { if (CoffSymbols[CoffIndex].Address && CoffSymbols[CoffIndex].FunctionOffset) { (*MergedSymbols)[*MergedSymbolCount] = CoffSymbols[CoffIndex]; (*MergedSymbolCount)++; } } qsort(*MergedSymbols, *MergedSymbolCount, sizeof(ROSSYM_ENTRY), (int (*)(const void *, const void *)) CompareSymEntry); return 0; } static PIMAGE_SECTION_HEADER FindSectionForRVA(DWORD RVA, unsigned NumberOfSections, PIMAGE_SECTION_HEADER SectionHeaders) { unsigned Section; for (Section = 0; Section < NumberOfSections; Section++) { if (SectionHeaders[Section].VirtualAddress <= RVA && RVA < SectionHeaders[Section].VirtualAddress + SectionHeaders[Section].Misc.VirtualSize) { return SectionHeaders + Section; } } return NULL; } static int ProcessRelocations(ULONG *ProcessedRelocsLength, void **ProcessedRelocs, void *RawData, PIMAGE_OPTIONAL_HEADER OptHeader, unsigned NumberOfSections, PIMAGE_SECTION_HEADER SectionHeaders) { PIMAGE_SECTION_HEADER RelocSectionHeader, TargetSectionHeader; PIMAGE_BASE_RELOCATION BaseReloc, End, AcceptedRelocs; int Found; if (OptHeader->NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_BASERELOC || OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0) { /* No relocation entries */ *ProcessedRelocsLength = 0; *ProcessedRelocs = NULL; return 0; } RelocSectionHeader = FindSectionForRVA(OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress, NumberOfSections, SectionHeaders); if (RelocSectionHeader == NULL) { fprintf(stderr, "Can't find section header for relocation data\n"); return 1; } *ProcessedRelocs = malloc(RelocSectionHeader->SizeOfRawData); if (*ProcessedRelocs == NULL) { fprintf(stderr, "Failed to allocate %u bytes for relocations\n", (unsigned int)RelocSectionHeader->SizeOfRawData); return 1; } *ProcessedRelocsLength = 0; BaseReloc = (PIMAGE_BASE_RELOCATION) ((char *) RawData + RelocSectionHeader->PointerToRawData + (OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress - RelocSectionHeader->VirtualAddress)); End = (PIMAGE_BASE_RELOCATION) ((char *) BaseReloc + OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); while (BaseReloc < End && BaseReloc->SizeOfBlock > 0) { TargetSectionHeader = FindSectionForRVA(BaseReloc->VirtualAddress, NumberOfSections, SectionHeaders); if (TargetSectionHeader != NULL) { AcceptedRelocs = *ProcessedRelocs; Found = 0; while (AcceptedRelocs < (PIMAGE_BASE_RELOCATION) ((char *) *ProcessedRelocs + *ProcessedRelocsLength) && !Found) { Found = BaseReloc->SizeOfBlock == AcceptedRelocs->SizeOfBlock && memcmp(BaseReloc, AcceptedRelocs, AcceptedRelocs->SizeOfBlock) == 0; AcceptedRelocs = (PIMAGE_BASE_RELOCATION) ((char *) AcceptedRelocs + AcceptedRelocs->SizeOfBlock); } if (!Found) { memcpy((char *) *ProcessedRelocs + *ProcessedRelocsLength, BaseReloc, BaseReloc->SizeOfBlock); *ProcessedRelocsLength += BaseReloc->SizeOfBlock; } } BaseReloc = (PIMAGE_BASE_RELOCATION)((char *) BaseReloc + BaseReloc->SizeOfBlock); } return 0; } static const BYTE* GetSectionName(void *StringsBase, const BYTE *SectionTitle) { if (SectionTitle[0] == '/') { int offset = atoi((char*)SectionTitle+1); return ((BYTE *)StringsBase) + offset; } else return SectionTitle; } static int CreateOutputFile(FILE *OutFile, void *InData, PIMAGE_DOS_HEADER InDosHeader, PIMAGE_FILE_HEADER InFileHeader, PIMAGE_OPTIONAL_HEADER InOptHeader, PIMAGE_SECTION_HEADER InSectionHeaders, ULONG RosSymLength, void *RosSymSection) { ULONG StartOfRawData; unsigned Section; void *OutHeader, *ProcessedRelocs, *PaddedRosSym, *Data; unsigned char *PaddedStringTable; PIMAGE_DOS_HEADER OutDosHeader; PIMAGE_FILE_HEADER OutFileHeader; PIMAGE_OPTIONAL_HEADER OutOptHeader; PIMAGE_SECTION_HEADER OutSectionHeaders, CurrentSectionHeader; DWORD CheckSum; ULONG Length, i; ULONG ProcessedRelocsLength; ULONG RosSymOffset, RosSymFileLength; ULONG PaddedStringTableLength; int InRelocSectionIndex; PIMAGE_SECTION_HEADER OutRelocSection; /* Each coff symbol is 18 bytes and the string table follows */ char *StringTable = (char *)InData + InFileHeader->PointerToSymbolTable + 18 * InFileHeader->NumberOfSymbols; ULONG StringTableLength = 0; ULONG StringTableLocation; StartOfRawData = 0; for (Section = 0; Section < InFileHeader->NumberOfSections; Section++) { const BYTE *SectionName = GetSectionName(StringTable, InSectionHeaders[Section].Name); if (InSectionHeaders[Section].Name[0] == '/') { StringTableLength = atoi((const char *)InSectionHeaders[Section].Name + 1) + strlen((const char *)SectionName) + 1; } if ((StartOfRawData == 0 || InSectionHeaders[Section].PointerToRawData < StartOfRawData) && InSectionHeaders[Section].PointerToRawData != 0 && (strncmp((char *) SectionName, ".stab", 5)) != 0 && (strncmp((char *) SectionName, ".debug_", 7)) != 0) { StartOfRawData = InSectionHeaders[Section].PointerToRawData; } } OutHeader = malloc(StartOfRawData); if (OutHeader == NULL) { fprintf(stderr, "Failed to allocate %u bytes for output file header\n", (unsigned int)StartOfRawData); return 1; } memset(OutHeader, '\0', StartOfRawData); OutDosHeader = (PIMAGE_DOS_HEADER) OutHeader; memcpy(OutDosHeader, InDosHeader, InDosHeader->e_lfanew + sizeof(ULONG)); OutFileHeader = (PIMAGE_FILE_HEADER)((char *) OutHeader + OutDosHeader->e_lfanew + sizeof(ULONG)); memcpy(OutFileHeader, InFileHeader, sizeof(IMAGE_FILE_HEADER)); OutFileHeader->PointerToSymbolTable = 0; OutFileHeader->NumberOfSymbols = 0; OutFileHeader->Characteristics &= ~(IMAGE_FILE_LINE_NUMS_STRIPPED | IMAGE_FILE_LOCAL_SYMS_STRIPPED | IMAGE_FILE_DEBUG_STRIPPED); OutOptHeader = (PIMAGE_OPTIONAL_HEADER)(OutFileHeader + 1); memcpy(OutOptHeader, InOptHeader, sizeof(IMAGE_OPTIONAL_HEADER)); OutOptHeader->CheckSum = 0; OutSectionHeaders = (PIMAGE_SECTION_HEADER)((char *) OutOptHeader + OutFileHeader->SizeOfOptionalHeader); if (ProcessRelocations(&ProcessedRelocsLength, &ProcessedRelocs, InData, InOptHeader, InFileHeader->NumberOfSections, InSectionHeaders)) { return 1; } if (InOptHeader->NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_BASERELOC || InOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0) { InRelocSectionIndex = -1; } else { InRelocSectionIndex = FindSectionForRVA(InOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress, InFileHeader->NumberOfSections, InSectionHeaders) - InSectionHeaders; } OutFileHeader->NumberOfSections = 0; CurrentSectionHeader = OutSectionHeaders; OutOptHeader->SizeOfImage = 0; RosSymOffset = 0; OutRelocSection = NULL; StringTableLocation = StartOfRawData; for (Section = 0; Section < InFileHeader->NumberOfSections; Section++) { const BYTE *SectionName = GetSectionName(StringTable, InSectionHeaders[Section].Name); if ((strncmp((char *) SectionName, ".stab", 5) != 0) && (strncmp((char *) SectionName, ".debug_", 7)) != 0) { *CurrentSectionHeader = InSectionHeaders[Section]; CurrentSectionHeader->PointerToLinenumbers = 0; CurrentSectionHeader->NumberOfLinenumbers = 0; if (OutOptHeader->SizeOfImage < CurrentSectionHeader->VirtualAddress + CurrentSectionHeader->Misc.VirtualSize) { OutOptHeader->SizeOfImage = ROUND_UP(CurrentSectionHeader->VirtualAddress + CurrentSectionHeader->Misc.VirtualSize, OutOptHeader->SectionAlignment); } if (RosSymOffset < CurrentSectionHeader->PointerToRawData + CurrentSectionHeader->SizeOfRawData) { RosSymOffset = CurrentSectionHeader->PointerToRawData + CurrentSectionHeader->SizeOfRawData; } if (Section == (ULONG)InRelocSectionIndex) { OutRelocSection = CurrentSectionHeader; } StringTableLocation = CurrentSectionHeader->PointerToRawData + CurrentSectionHeader->SizeOfRawData; OutFileHeader->NumberOfSections++; CurrentSectionHeader++; } } if (OutRelocSection == CurrentSectionHeader - 1) { OutOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = ProcessedRelocsLength; if (OutOptHeader->SizeOfImage == OutRelocSection->VirtualAddress + ROUND_UP(OutRelocSection->Misc.VirtualSize, OutOptHeader->SectionAlignment)) { OutOptHeader->SizeOfImage = OutRelocSection->VirtualAddress + ROUND_UP(ProcessedRelocsLength, OutOptHeader->SectionAlignment); } OutRelocSection->Misc.VirtualSize = ProcessedRelocsLength; if (RosSymOffset == OutRelocSection->PointerToRawData + OutRelocSection->SizeOfRawData) { RosSymOffset = OutRelocSection->PointerToRawData + ROUND_UP(ProcessedRelocsLength, OutOptHeader->FileAlignment); } OutRelocSection->SizeOfRawData = ROUND_UP(ProcessedRelocsLength, OutOptHeader->FileAlignment); } if (RosSymLength > 0) { RosSymFileLength = ROUND_UP(RosSymLength, OutOptHeader->FileAlignment); memcpy(CurrentSectionHeader->Name, ".rossym", 8); /* We're lucky: string is exactly 8 bytes long */ CurrentSectionHeader->Misc.VirtualSize = RosSymLength; CurrentSectionHeader->VirtualAddress = OutOptHeader->SizeOfImage; CurrentSectionHeader->SizeOfRawData = RosSymFileLength; CurrentSectionHeader->PointerToRawData = RosSymOffset; CurrentSectionHeader->PointerToRelocations = 0; CurrentSectionHeader->PointerToLinenumbers = 0; CurrentSectionHeader->NumberOfRelocations = 0; CurrentSectionHeader->NumberOfLinenumbers = 0; CurrentSectionHeader->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_LNK_REMOVE | IMAGE_SCN_TYPE_NOLOAD; OutOptHeader->SizeOfImage = ROUND_UP(CurrentSectionHeader->VirtualAddress + CurrentSectionHeader->Misc.VirtualSize, OutOptHeader->SectionAlignment); OutFileHeader->NumberOfSections++; PaddedRosSym = malloc(RosSymFileLength); if (PaddedRosSym == NULL) { fprintf(stderr, "Failed to allocate %u bytes for padded .rossym\n", (unsigned int)RosSymFileLength); return 1; } memcpy(PaddedRosSym, RosSymSection, RosSymLength); memset((char *) PaddedRosSym + RosSymLength, '\0', RosSymFileLength - RosSymLength); /* Position the string table after our new section */ StringTableLocation = RosSymOffset + RosSymFileLength; } else { PaddedRosSym = NULL; } /* Set the string table area in the header if we need it */ if (StringTableLength) { OutFileHeader->PointerToSymbolTable = StringTableLocation; OutFileHeader->NumberOfSymbols = 0; } CheckSum = 0; for (i = 0; i < StartOfRawData / 2; i++) { CheckSum += ((unsigned short*) OutHeader)[i]; CheckSum = 0xffff & (CheckSum + (CheckSum >> 16)); } Length = StartOfRawData; for (Section = 0; Section < OutFileHeader->NumberOfSections; Section++) { DWORD SizeOfRawData; if (OutRelocSection == OutSectionHeaders + Section) { Data = (void *) ProcessedRelocs; SizeOfRawData = ProcessedRelocsLength; } else if (RosSymLength > 0 && Section + 1 == OutFileHeader->NumberOfSections) { Data = (void *) PaddedRosSym; SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData; } else { Data = (void *) ((char *) InData + OutSectionHeaders[Section].PointerToRawData); SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData; } for (i = 0; i < SizeOfRawData / 2; i++) { CheckSum += ((unsigned short*) Data)[i]; CheckSum = 0xffff & (CheckSum + (CheckSum >> 16)); } Length += OutSectionHeaders[Section].SizeOfRawData; } if (OutFileHeader->PointerToSymbolTable) { int PaddingFrom = (OutFileHeader->PointerToSymbolTable + StringTableLength) % OutOptHeader->FileAlignment; int PaddingSize = PaddingFrom ? OutOptHeader->FileAlignment - PaddingFrom : 0; PaddedStringTableLength = StringTableLength + PaddingSize; PaddedStringTable = malloc(PaddedStringTableLength); /* COFF string section is preceeded by a length */ assert(sizeof(StringTableLength) == 4); memcpy(PaddedStringTable, &StringTableLength, sizeof(StringTableLength)); /* We just copy enough of the string table to contain the strings we want The string table length technically counts as part of the string table space itself. */ memcpy(PaddedStringTable + 4, StringTable + 4, StringTableLength - 4); memset(PaddedStringTable + StringTableLength, 0, PaddingSize); assert(OutFileHeader->PointerToSymbolTable % 2 == 0); for (i = 0; i < PaddedStringTableLength / 2; i++) { CheckSum += ((unsigned short*)PaddedStringTable)[i]; CheckSum = 0xffff & (CheckSum + (CheckSum >> 16)); } Length += PaddedStringTableLength; } else { PaddedStringTable = NULL; } CheckSum += Length; OutOptHeader->CheckSum = CheckSum; if (fwrite(OutHeader, 1, StartOfRawData, OutFile) != StartOfRawData) { perror("Error writing output header\n"); free(OutHeader); return 1; } for (Section = 0; Section < OutFileHeader->NumberOfSections; Section++) { if (OutSectionHeaders[Section].SizeOfRawData != 0) { DWORD SizeOfRawData; fseek(OutFile, OutSectionHeaders[Section].PointerToRawData, SEEK_SET); if (OutRelocSection == OutSectionHeaders + Section) { Data = (void *) ProcessedRelocs; SizeOfRawData = ProcessedRelocsLength; } else if (RosSymLength > 0 && Section + 1 == OutFileHeader->NumberOfSections) { Data = (void *) PaddedRosSym; SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData; } else { Data = (void *) ((char *) InData + OutSectionHeaders[Section].PointerToRawData); SizeOfRawData = OutSectionHeaders[Section].SizeOfRawData; } if (fwrite(Data, 1, SizeOfRawData, OutFile) != SizeOfRawData) { perror("Error writing section data\n"); free(PaddedRosSym); free(OutHeader); return 1; } } } if (PaddedStringTable) { fseek(OutFile, OutFileHeader->PointerToSymbolTable, SEEK_SET); fwrite(PaddedStringTable, 1, PaddedStringTableLength, OutFile); free(PaddedStringTable); } if (PaddedRosSym) { free(PaddedRosSym); } free(OutHeader); return 0; } int main(int argc, char* argv[]) { PSYMBOLFILE_HEADER SymbolFileHeader; PIMAGE_DOS_HEADER PEDosHeader; PIMAGE_FILE_HEADER PEFileHeader; PIMAGE_OPTIONAL_HEADER PEOptHeader; PIMAGE_SECTION_HEADER PESectionHeaders; ULONG ImageBase; void *StabBase; ULONG StabsLength; void *StabStringBase; ULONG StabStringsLength; void *CoffBase = NULL; ULONG CoffsLength; void *CoffStringBase = NULL; ULONG CoffStringsLength; char* path1; char* path2; FILE* out; void *StringBase = NULL; ULONG StringsLength = 0; ULONG StabSymbolsCount = 0; PROSSYM_ENTRY StabSymbols = NULL; ULONG CoffSymbolsCount = 0; PROSSYM_ENTRY CoffSymbols = NULL; ULONG MergedSymbolsCount = 0; PROSSYM_ENTRY MergedSymbols = NULL; size_t FileSize; void *FileData; ULONG RosSymLength; void *RosSymSection; DWORD module_base; void *file; char elfhdr[4] = { '\177', 'E', 'L', 'F' }; BOOLEAN UseDbgHelp = FALSE; int arg, argstate = 0; char *SourcePath = NULL; for (arg = 1; arg < argc; arg++) { switch (argstate) { default: argstate = -1; break; case 0: if (!strcmp(argv[arg], "-s")) { argstate = 1; } else { argstate = 2; path1 = convert_path(argv[arg]); } break; case 1: free(SourcePath); SourcePath = strdup(argv[arg]); argstate = 0; break; case 2: path2 = convert_path(argv[arg]); argstate = 3; break; } } if (argstate != 3) { fprintf(stderr, "Usage: rsym [-s ] \n"); exit(1); } FileData = load_file(path1, &FileSize); if (!FileData) { fprintf(stderr, "An error occured loading '%s'\n", path1); exit(1); } file = fopen(path1, "rb"); /* Check if MZ header exists */ PEDosHeader = (PIMAGE_DOS_HEADER) FileData; if (PEDosHeader->e_magic != IMAGE_DOS_MAGIC || PEDosHeader->e_lfanew == 0L) { /* Ignore elf */ if (!memcmp(PEDosHeader, elfhdr, sizeof(elfhdr))) exit(0); perror("Input file is not a PE image.\n"); free(FileData); exit(1); } /* Locate PE file header */ /* sizeof(ULONG) = sizeof(MAGIC) */ PEFileHeader = (PIMAGE_FILE_HEADER)((char *) FileData + PEDosHeader->e_lfanew + sizeof(ULONG)); /* Locate optional header */ assert(sizeof(ULONG) == 4); PEOptHeader = (PIMAGE_OPTIONAL_HEADER)(PEFileHeader + 1); ImageBase = PEOptHeader->ImageBase; /* Locate PE section headers */ PESectionHeaders = (PIMAGE_SECTION_HEADER)((char *) PEOptHeader + PEFileHeader->SizeOfOptionalHeader); if (GetStabInfo(FileData, PEFileHeader, PESectionHeaders, &StabsLength, &StabBase, &StabStringsLength, &StabStringBase)) { free(FileData); exit(1); } if (StabsLength == 0) { // SYMOPT_AUTO_PUBLICS // SYMOPT_FAVOR_COMPRESSED // SYMOPT_LOAD_ANYTHING // SYMOPT_LOAD_LINES SymSetOptions(0x10000 | 0x800000 | 0x40 | 0x10); SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES, TRUE); SymInitialize(FileData, ".", 0); module_base = SymLoadModule(FileData, file, path1, path1, 0, FileSize) & 0xffffffff; if (ConvertDbgHelp(FileData, module_base, SourcePath, &StabSymbolsCount, &StabSymbols, &StringsLength, &StringBase)) { free(FileData); exit(1); } UseDbgHelp = TRUE; SymUnloadModule(FileData, module_base); SymCleanup(FileData); } if (GetCoffInfo(FileData, PEFileHeader, PESectionHeaders, &CoffsLength, &CoffBase, &CoffStringsLength, &CoffStringBase)) { free(FileData); exit(1); } if (!UseDbgHelp) { StringBase = malloc(1 + StringsLength + CoffStringsLength + (CoffsLength / sizeof(ROSSYM_ENTRY)) * (E_SYMNMLEN + 1)); if (StringBase == NULL) { free(FileData); fprintf(stderr, "Failed to allocate memory for strings table\n"); exit(1); } /* Make offset 0 into an empty string */ *((char *) StringBase) = '\0'; StringsLength = 1; if (ConvertStabs(&StabSymbolsCount, &StabSymbols, &StringsLength, StringBase, StabsLength, StabBase, StabStringsLength, StabStringBase, ImageBase, PEFileHeader, PESectionHeaders)) { free(StringBase); free(FileData); fprintf(stderr, "Failed to allocate memory for strings table\n"); exit(1); } } else { StringBase = realloc(StringBase, StringsLength + CoffStringsLength); if (!StringBase) { free(FileData); fprintf(stderr, "Failed to allocate memory for strings table\n"); exit(1); } } if (ConvertCoffs(&CoffSymbolsCount, &CoffSymbols, &StringsLength, StringBase, CoffsLength, CoffBase, CoffStringsLength, CoffStringBase, ImageBase, PEFileHeader, PESectionHeaders)) { if (StabSymbols) { free(StabSymbols); } free(StringBase); free(FileData); exit(1); } if (MergeStabsAndCoffs(&MergedSymbolsCount, &MergedSymbols, StabSymbolsCount, StabSymbols, CoffSymbolsCount, CoffSymbols)) { if (CoffSymbols) { free(CoffSymbols); } if (StabSymbols) { free(StabSymbols); } free(StringBase); free(FileData); exit(1); } if (CoffSymbols) { free(CoffSymbols); } if (StabSymbols) { free(StabSymbols); } if (MergedSymbolsCount == 0) { RosSymLength = 0; RosSymSection = NULL; } else { RosSymLength = sizeof(SYMBOLFILE_HEADER) + MergedSymbolsCount * sizeof(ROSSYM_ENTRY) + StringsLength; RosSymSection = malloc(RosSymLength); if (RosSymSection == NULL) { free(MergedSymbols); free(StringBase); free(FileData); fprintf(stderr, "Unable to allocate memory for .rossym section\n"); exit(1); } memset(RosSymSection, '\0', RosSymLength); SymbolFileHeader = (PSYMBOLFILE_HEADER)RosSymSection; SymbolFileHeader->SymbolsOffset = sizeof(SYMBOLFILE_HEADER); SymbolFileHeader->SymbolsLength = MergedSymbolsCount * sizeof(ROSSYM_ENTRY); SymbolFileHeader->StringsOffset = SymbolFileHeader->SymbolsOffset + SymbolFileHeader->SymbolsLength; SymbolFileHeader->StringsLength = StringsLength; memcpy((char *) RosSymSection + SymbolFileHeader->SymbolsOffset, MergedSymbols, SymbolFileHeader->SymbolsLength); memcpy((char *) RosSymSection + SymbolFileHeader->StringsOffset, StringBase, SymbolFileHeader->StringsLength); free(MergedSymbols); } free(StringBase); out = fopen(path2, "wb"); if (out == NULL) { perror("Cannot open output file"); free(RosSymSection); free(FileData); exit(1); } if (CreateOutputFile(out, FileData, PEDosHeader, PEFileHeader, PEOptHeader, PESectionHeaders, RosSymLength, RosSymSection)) { fclose(out); if (RosSymSection) { free(RosSymSection); } free(FileData); exit(1); } fclose(out); if (RosSymSection) { free(RosSymSection); } free(FileData); return 0; } /* EOF */