Commit 01aa7137 authored by Eric Pouech's avatar Eric Pouech Committed by Alexandre Julliard

Memory consumption optimization while loading ELF debug info:

- don't map twice an ELF file for symbol lookup (in non deferred mode) - no longer entirely map an ELF file into memory, but only the sections we need. Added support for loading ELF modules thru SymLoadModule in a non life process. Factorisation of code for ELF module handling. Fixes to ELF symbol loading - drops symbols from symtab which are neither funcs nor global variables - fixes some incorrect size computation for latest GCC versions. Several cleanups and fixes.
parent 13abcb0a
...@@ -37,6 +37,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); ...@@ -37,6 +37,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
* but those values are not directly usable from a debugger (that's why, I * but those values are not directly usable from a debugger (that's why, I
* assume, that we have also to define constants for enum values, as * assume, that we have also to define constants for enum values, as
* Codeview does BTW. * Codeview does BTW.
* + SymGetType(TI_GET_LENGTH) takes a ULONG64 (yurk, ugly)
* - SymGetLine{Next|Prev} don't work as expected (they don't seem to work across
* functions, and even across function blocks...). Basically, for *Next* to work
* it requires an address after the prolog of the func (the base address of the
* func doesn't work)
* - most options (dbghelp_options) are not used (loading lines...) * - most options (dbghelp_options) are not used (loading lines...)
* - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when * - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when
* we're supposed to use RE, it doesn't make use of our hash tables. Therefore, * we're supposed to use RE, it doesn't make use of our hash tables. Therefore,
...@@ -45,7 +50,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); ...@@ -45,7 +50,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
* - msc: * - msc:
* + we should add parameters' types to the function's signature * + we should add parameters' types to the function's signature
* while processing a function's parameters * while processing a function's parameters
* + get rid of MSC reading FIXME:s (lots of types are not defined) * + add support for function-less labels (as MSC seems to define them)
* + C++ management * + C++ management
* - stabs: * - stabs:
* + when, in a same module, the same definition is used in several compilation * + when, in a same module, the same definition is used in several compilation
...@@ -132,25 +137,10 @@ BOOL WINAPI SymGetSearchPath(HANDLE hProcess, LPSTR szSearchPath, ...@@ -132,25 +137,10 @@ BOOL WINAPI SymGetSearchPath(HANDLE hProcess, LPSTR szSearchPath,
* SymInitialize helper: loads in dbghelp all known (and loaded modules) * SymInitialize helper: loads in dbghelp all known (and loaded modules)
* this assumes that hProcess is a handle on a valid process * this assumes that hProcess is a handle on a valid process
*/ */
static BOOL process_invade(HANDLE hProcess) static BOOL WINAPI process_invade_cb(char* name, DWORD base, DWORD size, void* user)
{ {
HMODULE hMods[256]; SymLoadModule((HANDLE)user, 0, name, NULL, base, size);
char img[256]; return TRUE;
DWORD i, sz;
MODULEINFO mi;
if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &sz))
return FALSE; /* FIXME should grow hMods */
for (i = 0; i < sz / sizeof(HMODULE); i++)
{
if (!GetModuleInformation(hProcess, hMods[i], &mi, sizeof(mi)) ||
!GetModuleFileNameExA(hProcess, hMods[i], img, sizeof(img)) ||
!SymLoadModule(hProcess, 0, img, NULL, (DWORD)mi.lpBaseOfDll, mi.SizeOfImage))
return FALSE;
}
return sz != 0;
} }
/****************************************************************** /******************************************************************
...@@ -235,9 +225,10 @@ BOOL WINAPI SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProc ...@@ -235,9 +225,10 @@ BOOL WINAPI SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProc
SymCleanup(hProcess); SymCleanup(hProcess);
return FALSE; return FALSE;
} }
process_invade(hProcess); EnumerateLoadedModules(hProcess, process_invade_cb, (void*)hProcess);
elf_synchronize_module_list(pcs); elf_synchronize_module_list(pcs);
} }
return TRUE; return TRUE;
} }
......
...@@ -293,9 +293,12 @@ extern struct process* process_find_by_handle(HANDLE hProcess); ...@@ -293,9 +293,12 @@ extern struct process* process_find_by_handle(HANDLE hProcess);
extern HANDLE hMsvcrt; extern HANDLE hMsvcrt;
/* elf_module.c */ /* elf_module.c */
extern BOOL elf_load_debug_info(struct module* module); typedef BOOL (*elf_enum_modules_cb)(const char*, unsigned long addr, void* user);
extern BOOL elf_enum_modules(HANDLE hProc, elf_enum_modules_cb, void*);
struct elf_file_map;
extern BOOL elf_load_debug_info(struct module* module, struct elf_file_map* fmap);
extern struct module* extern struct module*
elf_load_module(struct process* pcs, const char* name); elf_load_module(struct process* pcs, const char* name, unsigned long);
extern BOOL elf_read_wine_loader_dbg_info(struct process* pcs); extern BOOL elf_read_wine_loader_dbg_info(struct process* pcs);
extern BOOL elf_synchronize_module_list(struct process* pcs); extern BOOL elf_synchronize_module_list(struct process* pcs);
......
...@@ -89,6 +89,25 @@ struct elf_info ...@@ -89,6 +89,25 @@ struct elf_info
struct module* module; /* OUT loaded module (if ELF_INFO_MODULE is set) */ struct module* module; /* OUT loaded module (if ELF_INFO_MODULE is set) */
}; };
#define NO_MAP ((const void*)0xffffffff)
/* structure holding information while handling an ELF image
* allows one by one section mapping for memory savings
*/
struct elf_file_map
{
Elf32_Ehdr elfhdr;
size_t elf_size;
size_t elf_start;
struct
{
Elf32_Shdr shdr;
const char* mapped;
}* sect;
int fd;
unsigned with_crc;
unsigned long crc;
};
struct symtab_elt struct symtab_elt
{ {
struct hash_table_elt ht_elt; struct hash_table_elt ht_elt;
...@@ -106,14 +125,127 @@ struct thunk_area ...@@ -106,14 +125,127 @@ struct thunk_area
}; };
/****************************************************************** /******************************************************************
* elf_map_section
*
* Maps a single section into memory from a ELF file
*/
static const char* elf_map_section(struct elf_file_map* fmap, int sidx)
{
unsigned pgsz = getpagesize();
unsigned ofst, size;
if (sidx >= fmap->elfhdr.e_shnum ||
fmap->sect[sidx].shdr.sh_type == SHT_NOBITS)
return NO_MAP;
/* align required information on page size (we assume pagesize is a power of 2) */
ofst = fmap->sect[sidx].shdr.sh_offset & ~(pgsz - 1);
size = (fmap->sect[sidx].shdr.sh_offset + fmap->sect[sidx].shdr.sh_size + pgsz - 1) & ~(pgsz - 1);
fmap->sect[sidx].mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fmap->fd, ofst);
if (fmap->sect[sidx].mapped == NO_MAP) return NO_MAP;
return fmap->sect[sidx].mapped + (fmap->sect[sidx].shdr.sh_offset & (pgsz - 1));
}
/******************************************************************
* elf_unmap_section
*
* Unmaps a single section from memory
*/
static void elf_unmap_section(struct elf_file_map* fmap, int sidx)
{
if (sidx < fmap->elfhdr.e_shnum && fmap->sect[sidx].mapped != NO_MAP)
{
munmap((char*)fmap->sect[sidx].mapped, fmap->sect[sidx].shdr.sh_size);
fmap->sect[sidx].mapped = NO_MAP;
}
}
/******************************************************************
* elf_map_file
*
* Maps a ELF file into memory (and checks it's a real ELF file)
*/
static BOOL elf_map_file(const char* filename, struct elf_file_map* fmap)
{
static const BYTE elf_signature[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 };
struct stat statbuf;
int i;
Elf32_Phdr phdr;
unsigned tmp, page_mask = getpagesize() - 1;
fmap->fd = -1;
fmap->with_crc = 0;
/* check that the file exists, and that the module hasn't been loaded yet */
if (stat(filename, &statbuf) == -1 || S_ISDIR(statbuf.st_mode)) return FALSE;
/* Now open the file, so that we can mmap() it. */
if ((fmap->fd = open(filename, O_RDONLY)) == -1) return FALSE;
if (read(fmap->fd, &fmap->elfhdr, sizeof(fmap->elfhdr)) != sizeof(fmap->elfhdr))
return FALSE;
/* and check for an ELF header */
if (memcmp(fmap->elfhdr.e_ident,
elf_signature, sizeof(elf_signature))) return FALSE;
fmap->sect = HeapAlloc(GetProcessHeap(), 0, fmap->elfhdr.e_shnum * sizeof(fmap->sect[0]));
if (!fmap->sect) return FALSE;
lseek(fmap->fd, fmap->elfhdr.e_shoff, SEEK_SET);
for (i = 0; i < fmap->elfhdr.e_shnum; i++)
{
read(fmap->fd, &fmap->sect[i].shdr, sizeof(fmap->sect[i].shdr));
fmap->sect[i].mapped = NO_MAP;
}
/* grab size of module once loaded in memory */
lseek(fmap->fd, fmap->elfhdr.e_phoff, SEEK_SET);
fmap->elf_size = 0;
fmap->elf_start = ~0L;
for (i = 0; i < fmap->elfhdr.e_phnum; i++)
{
if (read(fmap->fd, &phdr, sizeof(phdr)) == sizeof(phdr) &&
phdr.p_type == PT_LOAD)
{
tmp = (phdr.p_vaddr + phdr.p_memsz + page_mask) & ~page_mask;
if (fmap->elf_size < tmp) fmap->elf_size = tmp;
if (phdr.p_vaddr < fmap->elf_start) fmap->elf_start = phdr.p_vaddr;
}
}
/* if non relocatable ELF, then remove fixed address from computation
* otherwise, all addresses are zero based and start has no effect
*/
fmap->elf_size -= fmap->elf_start;
return TRUE;
}
/******************************************************************
* elf_unmap_file
*
* Unmaps a ELF file from memory (previously mapped with elf_map_file)
*/
static void elf_unmap_file(struct elf_file_map* fmap)
{
if (fmap->fd != -1)
{
int i;
for (i = 0; i < fmap->elfhdr.e_shnum; i++)
{
elf_unmap_section(fmap, i);
}
HeapFree(GetProcessHeap(), 0, fmap->sect);
close(fmap->fd);
}
}
/******************************************************************
* elf_hash_symtab * elf_hash_symtab
* *
* creating an internal hash table to ease use ELF symtab information lookup * creating an internal hash table to ease use ELF symtab information lookup
*/ */
static void elf_hash_symtab(const struct module* module, struct pool* pool, static void elf_hash_symtab(const struct module* module, struct pool* pool,
struct hash_table* ht_symtab, const char* map_addr, struct hash_table* ht_symtab, struct elf_file_map* fmap,
const Elf32_Shdr* symtab, const Elf32_Shdr* strtab, int symtab_idx, struct thunk_area* thunks)
unsigned num_areas, struct thunk_area* thunks)
{ {
int i, j, nsym; int i, j, nsym;
const char* strp; const char* strp;
...@@ -123,11 +255,13 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool, ...@@ -123,11 +255,13 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool,
const Elf32_Sym* symp; const Elf32_Sym* symp;
struct symtab_elt* ste; struct symtab_elt* ste;
symp = (const Elf32_Sym*)(map_addr + symtab->sh_offset); symp = (const Elf32_Sym*)elf_map_section(fmap, symtab_idx);
nsym = symtab->sh_size / sizeof(*symp); strp = elf_map_section(fmap, fmap->sect[symtab_idx].shdr.sh_link);
strp = (const char*)(map_addr + strtab->sh_offset); if (symp == NO_MAP || strp == NO_MAP) return;
nsym = fmap->sect[symtab_idx].shdr.sh_size / sizeof(*symp);
for (j = 0; j < num_areas; j++) for (j = 0; thunks[j].symname; j++)
thunks[j].rva_start = thunks[j].rva_end = 0; thunks[j].rva_start = thunks[j].rva_end = 0;
for (i = 0; i < nsym; i++, symp++) for (i = 0; i < nsym; i++, symp++)
...@@ -135,7 +269,10 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool, ...@@ -135,7 +269,10 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool,
/* Ignore certain types of entries which really aren't of that much /* Ignore certain types of entries which really aren't of that much
* interest. * interest.
*/ */
if (ELF32_ST_TYPE(symp->st_info) == STT_SECTION || symp->st_shndx == SHN_UNDEF) if ((ELF32_ST_TYPE(symp->st_info) != STT_FILE &&
ELF32_ST_TYPE(symp->st_info) != STT_OBJECT &&
ELF32_ST_TYPE(symp->st_info) != STT_FUNC) ||
symp->st_shndx == SHN_UNDEF)
{ {
continue; continue;
} }
...@@ -147,7 +284,7 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool, ...@@ -147,7 +284,7 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool,
filename = symname; filename = symname;
continue; continue;
} }
for (j = 0; j < num_areas; j++) for (j = 0; thunks[j].symname; j++)
{ {
if (!strcmp(symname, thunks[j].symname)) if (!strcmp(symname, thunks[j].symname))
{ {
...@@ -156,7 +293,7 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool, ...@@ -156,7 +293,7 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool,
break; break;
} }
} }
if (j < num_areas) continue; if (thunks[j].symname) continue;
/* FIXME: we don't need to handle them (GCC internals) /* FIXME: we don't need to handle them (GCC internals)
* Moreover, they screw up our symbol lookup :-/ * Moreover, they screw up our symbol lookup :-/
...@@ -165,7 +302,8 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool, ...@@ -165,7 +302,8 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool,
continue; continue;
ste = pool_alloc(pool, sizeof(*ste)); ste = pool_alloc(pool, sizeof(*ste));
/* GCC seems to emit, in some cases, a .<digit>+ suffix. ste->ht_elt.name = symname;
/* GCC emits, in some cases, a .<digit>+ suffix.
* This is used for static variable inside functions, so * This is used for static variable inside functions, so
* that we can have several such variables with same name in * that we can have several such variables with same name in
* the same compilation unit * the same compilation unit
...@@ -173,10 +311,9 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool, ...@@ -173,10 +311,9 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool,
* of it in stabs parsing) * of it in stabs parsing)
*/ */
ptr = symname + strlen(symname) - 1; ptr = symname + strlen(symname) - 1;
ste->ht_elt.name = symname;
if (isdigit(*ptr)) if (isdigit(*ptr))
{ {
while (*ptr >= '0' && *ptr <= '9' && ptr >= symname) ptr--; while (isdigit(*ptr) && ptr >= symname) ptr--;
if (ptr > symname && *ptr == '.') if (ptr > symname && *ptr == '.')
{ {
char* n = pool_alloc(pool, ptr - symname + 1); char* n = pool_alloc(pool, ptr - symname + 1);
...@@ -190,6 +327,10 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool, ...@@ -190,6 +327,10 @@ static void elf_hash_symtab(const struct module* module, struct pool* pool,
ste->used = 0; ste->used = 0;
hash_table_add(ht_symtab, &ste->ht_elt); hash_table_add(ht_symtab, &ste->ht_elt);
} }
/* as we added in the ht_symtab pointers to the symbols themselves,
* we cannot unmap yet the sections, it will be done when we're over
* with this ELF file
*/
} }
/****************************************************************** /******************************************************************
...@@ -253,7 +394,7 @@ static const Elf32_Sym* elf_lookup_symtab(const struct module* module, ...@@ -253,7 +394,7 @@ static const Elf32_Sym* elf_lookup_symtab(const struct module* module,
} }
if (!result && !(result = weak_result)) if (!result && !(result = weak_result))
{ {
FIXME("Couldn't find symbol %s.%s in symtab\n", FIXME("Couldn't find symbol %s!%s in symtab\n",
module->module.ModuleName, name); module->module.ModuleName, name);
return NULL; return NULL;
} }
...@@ -289,10 +430,20 @@ static void elf_finish_stabs_info(struct module* module, struct hash_table* symt ...@@ -289,10 +430,20 @@ static void elf_finish_stabs_info(struct module* module, struct hash_table* symt
((struct symt_function*)sym)->container); ((struct symt_function*)sym)->container);
if (symp) if (symp)
{ {
if (((struct symt_function*)sym)->address != module->elf_info->elf_addr &&
((struct symt_function*)sym)->address != module->elf_info->elf_addr + symp->st_value)
FIXME("Changing address for %p/%s!%s from %08lx to %08lx\n",
sym, module->module.ModuleName, sym->hash_elt.name,
((struct symt_function*)sym)->address, module->elf_info->elf_addr + symp->st_value);
if (((struct symt_function*)sym)->size && ((struct symt_function*)sym)->size != symp->st_size)
FIXME("Changing size for %p/%s!%s from %08lx to %08x\n",
sym, module->module.ModuleName, sym->hash_elt.name,
((struct symt_function*)sym)->size, symp->st_size);
((struct symt_function*)sym)->address = module->elf_info->elf_addr + ((struct symt_function*)sym)->address = module->elf_info->elf_addr +
symp->st_value; symp->st_value;
((struct symt_function*)sym)->size = symp->st_size; ((struct symt_function*)sym)->size = symp->st_size;
} else FIXME("Couldn't find %s\n", sym->hash_elt.name); } else FIXME("Couldn't find %s!%s\n", module->module.ModuleName, sym->hash_elt.name);
break; break;
case SymTagData: case SymTagData:
switch (((struct symt_data*)sym)->kind) switch (((struct symt_data*)sym)->kind)
...@@ -305,11 +456,16 @@ static void elf_finish_stabs_info(struct module* module, struct hash_table* symt ...@@ -305,11 +456,16 @@ static void elf_finish_stabs_info(struct module* module, struct hash_table* symt
((struct symt_data*)sym)->container); ((struct symt_data*)sym)->container);
if (symp) if (symp)
{ {
if (((struct symt_data*)sym)->u.address != module->elf_info->elf_addr &&
((struct symt_data*)sym)->u.address != module->elf_info->elf_addr + symp->st_value)
FIXME("Changing address for %p/%s!%s from %08lx to %08lx\n",
sym, module->module.ModuleName, sym->hash_elt.name,
((struct symt_function*)sym)->address, module->elf_info->elf_addr + symp->st_value);
((struct symt_data*)sym)->u.address = module->elf_info->elf_addr + ((struct symt_data*)sym)->u.address = module->elf_info->elf_addr +
symp->st_value; symp->st_value;
((struct symt_data*)sym)->kind = (ELF32_ST_BIND(symp->st_info) == STB_LOCAL) ? ((struct symt_data*)sym)->kind = (ELF32_ST_BIND(symp->st_info) == STB_LOCAL) ?
DataIsFileStatic : DataIsGlobal; DataIsFileStatic : DataIsGlobal;
} } else FIXME("Couldn't find %s!%s\n", module->module.ModuleName, sym->hash_elt.name);
break; break;
default:; default:;
} }
...@@ -381,16 +537,20 @@ static int elf_new_wine_thunks(struct module* module, struct hash_table* ht_symt ...@@ -381,16 +537,20 @@ static int elf_new_wine_thunks(struct module* module, struct hash_table* ht_symt
* used yet (ie we have no debug information on them) * used yet (ie we have no debug information on them)
* That's the case, for example, of the .spec.c files * That's the case, for example, of the .spec.c files
*/ */
if (ELF32_ST_TYPE(ste->symp->st_info) == STT_FUNC) switch (ELF32_ST_TYPE(ste->symp->st_info))
{ {
case STT_FUNC:
symt_new_function(module, compiland, ste->ht_elt.name, symt_new_function(module, compiland, ste->ht_elt.name,
addr, ste->symp->st_size, NULL); addr, ste->symp->st_size, NULL);
} break;
else case STT_OBJECT:
{
symt_new_global_variable(module, compiland, ste->ht_elt.name, symt_new_global_variable(module, compiland, ste->ht_elt.name,
ELF32_ST_BIND(ste->symp->st_info) == STB_LOCAL, ELF32_ST_BIND(ste->symp->st_info) == STB_LOCAL,
addr, ste->symp->st_size, NULL); addr, ste->symp->st_size, NULL);
break;
default:
FIXME("Shouldn't happen\n");
break;
} }
/* FIXME: this is a hack !!! /* FIXME: this is a hack !!!
* we are adding new symbols, but as we're parsing a symbol table * we are adding new symbols, but as we're parsing a symbol table
...@@ -410,7 +570,6 @@ static int elf_new_wine_thunks(struct module* module, struct hash_table* ht_symt ...@@ -410,7 +570,6 @@ static int elf_new_wine_thunks(struct module* module, struct hash_table* ht_symt
symt_get_info(&module->addr_sorttab[idx]->symt, TI_GET_LENGTH, &xsize); symt_get_info(&module->addr_sorttab[idx]->symt, TI_GET_LENGTH, &xsize);
symt_get_info(&module->addr_sorttab[idx]->symt, TI_GET_DATAKIND, &kind); symt_get_info(&module->addr_sorttab[idx]->symt, TI_GET_DATAKIND, &kind);
#if 0
/* If none of symbols has a correct size, we consider they are both markers /* If none of symbols has a correct size, we consider they are both markers
* Hence, we can silence this warning * Hence, we can silence this warning
* Also, we check that we don't have two symbols, one local, the other * Also, we check that we don't have two symbols, one local, the other
...@@ -423,7 +582,6 @@ static int elf_new_wine_thunks(struct module* module, struct hash_table* ht_symt ...@@ -423,7 +582,6 @@ static int elf_new_wine_thunks(struct module* module, struct hash_table* ht_symt
ste->ht_elt.name, addr, ste->symp->st_size, ste->ht_elt.name, addr, ste->symp->st_size,
module->addr_sorttab[idx]->hash_elt.name, module->addr_sorttab[idx]->hash_elt.name,
wine_dbgstr_longlong(xaddr), xsize); wine_dbgstr_longlong(xaddr), xsize);
#endif
} }
} }
} }
...@@ -529,7 +687,7 @@ static int elf_new_public_symbols(struct module* module, struct hash_table* symt ...@@ -529,7 +687,7 @@ static int elf_new_public_symbols(struct module* module, struct hash_table* symt
/* using byte-swap instructions. */ /* using byte-swap instructions. */
static DWORD calc_crc32(const unsigned char *buf, size_t len) static DWORD calc_crc32(struct elf_file_map* fmap)
{ {
#define UPDC32(octet,crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) #define UPDC32(octet,crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8))
static const DWORD crc_32_tab[] = static const DWORD crc_32_tab[] =
...@@ -578,37 +736,38 @@ static DWORD calc_crc32(const unsigned char *buf, size_t len) ...@@ -578,37 +736,38 @@ static DWORD calc_crc32(const unsigned char *buf, size_t len)
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
}; };
size_t i; int i, r;
DWORD crc = ~0; unsigned char buffer[256];
for(i = 0; i < len; i++) DWORD crc = ~0;
crc = UPDC32(buf[i], crc);
lseek(fmap->fd, 0, SEEK_SET);
while ((r = read(fmap->fd, buffer, sizeof(buffer))) > 0)
{
for (i = 0; i < r; i++) crc = UPDC32(buffer[i], crc);
}
return ~crc; return ~crc;
#undef UPDC32 #undef UPDC32
} }
/****************************************************************** /******************************************************************
* elf_load_debug_info_from_file * elf_load_debug_info_from_map
* *
* Loads the symbolic information from ELF module stored in 'file' * Loads the symbolic information from ELF module which mapping is described
* in fmap
* the module has been loaded at 'load_offset' address, so symbols' address * the module has been loaded at 'load_offset' address, so symbols' address
* relocation is performed. crc optionally points to the CRC of the debug file * relocation is performed.
* to load. * CRC is checked if fmap->with_crc is TRUE
* returns * returns
* 0 if the file doesn't contain symbolic info (or this info cannot be * 0 if the file doesn't contain symbolic info (or this info cannot be
* read or parsed) * read or parsed)
* 1 on success * 1 on success
*/ */
static BOOL elf_load_debug_info_from_file( static BOOL elf_load_debug_info_from_map(struct module* module,
struct module* module, const char* file, struct pool* pool, struct elf_file_map* fmap,
struct hash_table* ht_symtab, const DWORD *crc) struct pool* pool,
struct hash_table* ht_symtab)
{ {
BOOL ret = FALSE; BOOL ret = FALSE;
char* addr = (char*)0xffffffff;
int fd = -1;
struct stat statbuf;
const Elf32_Ehdr* ehptr;
const Elf32_Shdr* spnt;
const char* shstrtab; const char* shstrtab;
int i; int i;
int symtab_sect, dynsym_sect, stab_sect, stabstr_sect, debug_sect, debuglink_sect; int symtab_sect, dynsym_sect, stab_sect, stabstr_sect, debug_sect, debuglink_sect;
...@@ -622,35 +781,15 @@ static BOOL elf_load_debug_info_from_file( ...@@ -622,35 +781,15 @@ static BOOL elf_load_debug_info_from_file(
{"__wine_spec_thunk_data_16", -16, 0, 0}, /* 16 => 32 thunks */ {"__wine_spec_thunk_data_16", -16, 0, 0}, /* 16 => 32 thunks */
{"__wine_spec_thunk_text_32", -32, 0, 0}, /* 32 => 16 thunks */ {"__wine_spec_thunk_text_32", -32, 0, 0}, /* 32 => 16 thunks */
{"__wine_spec_thunk_data_32", -32, 0, 0}, /* 32 => 16 thunks */ {"__wine_spec_thunk_data_32", -32, 0, 0}, /* 32 => 16 thunks */
{NULL, 0, 0, 0}
}; };
if (module->type != DMT_ELF || !module->elf_info) if (fmap->with_crc && (fmap->crc != calc_crc32(fmap)))
{
ERR("Bad elf module '%s'\n", module->module.LoadedImageName);
return FALSE;
}
TRACE("%s\n", file);
/* check that the file exists, and that the module hasn't been loaded yet */
if (stat(file, &statbuf) == -1) goto leave;
if (S_ISDIR(statbuf.st_mode)) goto leave;
/*
* Now open the file, so that we can mmap() it.
*/
if ((fd = open(file, O_RDONLY)) == -1) goto leave;
/*
* Now mmap() the file.
*/
addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == (char*)0xffffffff) goto leave;
if (crc && (*crc != calc_crc32(addr, statbuf.st_size)))
{ {
ERR("Bad CRC for file %s\n", file); ERR("Bad CRC for module %s (got %08lx while expecting %08lx)\n",
module->module.ImageName, calc_crc32(fmap), fmap->crc);
/* we don't tolerate mis-matched files */ /* we don't tolerate mis-matched files */
goto leave; return FALSE;
} }
/* /*
...@@ -658,29 +797,30 @@ static BOOL elf_load_debug_info_from_file( ...@@ -658,29 +797,30 @@ static BOOL elf_load_debug_info_from_file(
* this thing. We need the main executable header, and the section * this thing. We need the main executable header, and the section
* table. * table.
*/ */
ehptr = (Elf32_Ehdr*)addr; shstrtab = elf_map_section(fmap, fmap->elfhdr.e_shstrndx);
spnt = (Elf32_Shdr*)(addr + ehptr->e_shoff); if (shstrtab == NO_MAP) return FALSE;
shstrtab = (addr + spnt[ehptr->e_shstrndx].sh_offset);
symtab_sect = dynsym_sect = stab_sect = stabstr_sect = debug_sect = debuglink_sect = -1; symtab_sect = dynsym_sect = stab_sect = stabstr_sect = debug_sect = debuglink_sect = -1;
for (i = 0; i < ehptr->e_shnum; i++) for (i = 0; i < fmap->elfhdr.e_shnum; i++)
{ {
if (strcmp(shstrtab + spnt[i].sh_name, ".stab") == 0) if (strcmp(shstrtab + fmap->sect[i].shdr.sh_name, ".stab") == 0)
stab_sect = i; stab_sect = i;
if (strcmp(shstrtab + spnt[i].sh_name, ".stabstr") == 0) if (strcmp(shstrtab + fmap->sect[i].shdr.sh_name, ".stabstr") == 0)
stabstr_sect = i; stabstr_sect = i;
if (strcmp(shstrtab + spnt[i].sh_name, ".debug_info") == 0) if (strcmp(shstrtab + fmap->sect[i].shdr.sh_name, ".debug_info") == 0)
debug_sect = i; debug_sect = i;
if (strcmp(shstrtab + spnt[i].sh_name, ".gnu_debuglink") == 0) if (strcmp(shstrtab + fmap->sect[i].shdr.sh_name, ".gnu_debuglink") == 0)
debuglink_sect = i; debuglink_sect = i;
if ((strcmp(shstrtab + spnt[i].sh_name, ".symtab") == 0) && if ((strcmp(shstrtab + fmap->sect[i].shdr.sh_name, ".symtab") == 0) &&
(spnt[i].sh_type == SHT_SYMTAB)) (fmap->sect[i].shdr.sh_type == SHT_SYMTAB))
symtab_sect = i; symtab_sect = i;
if ((strcmp(shstrtab + spnt[i].sh_name, ".dynsym") == 0) && if ((strcmp(shstrtab + fmap->sect[i].shdr.sh_name, ".dynsym") == 0) &&
(spnt[i].sh_type == SHT_DYNSYM)) (fmap->sect[i].shdr.sh_type == SHT_DYNSYM))
dynsym_sect = i; dynsym_sect = i;
} }
elf_unmap_section(fmap, fmap->elfhdr.e_shstrndx);
shstrtab = NULL;
if (symtab_sect == -1) if (symtab_sect == -1)
{ {
...@@ -688,31 +828,38 @@ static BOOL elf_load_debug_info_from_file( ...@@ -688,31 +828,38 @@ static BOOL elf_load_debug_info_from_file(
* section instead. It'll contain less (relevant) information, * section instead. It'll contain less (relevant) information,
* but it'll be better than nothing * but it'll be better than nothing
*/ */
if (dynsym_sect == -1) goto leave; if (dynsym_sect == -1) return FALSE;
symtab_sect = dynsym_sect; symtab_sect = dynsym_sect;
} }
module->module.SymType = SymExport; module->module.SymType = SymExport;
/* create a hash table for the symtab */ /* create a hash table for the symtab */
elf_hash_symtab(module, pool, ht_symtab, addr, elf_hash_symtab(module, pool, ht_symtab, fmap, symtab_sect, thunks);
spnt + symtab_sect, spnt + spnt[symtab_sect].sh_link,
sizeof(thunks) / sizeof(thunks[0]), thunks);
if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY)) if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY))
{ {
if (stab_sect != -1 && stabstr_sect != -1) if (stab_sect != -1 && stabstr_sect != -1)
{ {
/* OK, now just parse all of the stabs. */ const char* stab;
ret = stabs_parse(module, module->elf_info->elf_addr, const char* stabstr;
addr + spnt[stab_sect].sh_offset,
spnt[stab_sect].sh_size, stab = elf_map_section(fmap, stab_sect);
addr + spnt[stabstr_sect].sh_offset, stabstr = elf_map_section(fmap, stabstr_sect);
spnt[stabstr_sect].sh_size); if (stab != NO_MAP && stabstr != NO_MAP)
{
/* OK, now just parse all of the stabs. */
ret = stabs_parse(module, module->elf_info->elf_addr,
stab, fmap->sect[stab_sect].shdr.sh_size,
stabstr, fmap->sect[stabstr_sect].shdr.sh_size);
}
elf_unmap_section(fmap, stab_sect);
elf_unmap_section(fmap, stabstr_sect);
if (!ret) if (!ret)
{ {
WARN("Couldn't read correctly read stabs\n"); WARN("Couldn't correctly read stabs\n");
goto leave; return FALSE;
} }
/* and fill in the missing information for stabs */ /* and fill in the missing information for stabs */
elf_finish_stabs_info(module, ht_symtab); elf_finish_stabs_info(module, ht_symtab);
...@@ -724,14 +871,25 @@ static BOOL elf_load_debug_info_from_file( ...@@ -724,14 +871,25 @@ static BOOL elf_load_debug_info_from_file(
} }
else if (debuglink_sect != -1) else if (debuglink_sect != -1)
{ {
DWORD crc; const char* dbg_link;
const char * file = (const char *)(addr + spnt[debuglink_sect].sh_offset); struct elf_file_map fmap_link;
/* crc is stored after the null terminated file string rounded
* up to the next 4 byte boundary */ dbg_link = elf_map_section(fmap, debuglink_sect);
crc = *(const DWORD *)(file + ((DWORD_PTR)(strlen(file) + 4) & ~3)); /* The content of a debug link section is:
ret = elf_load_debug_info_from_file(module, file, pool, ht_symtab, &crc); * 1/ a NULL terminated string, containing the file name for the debug info
if (!ret) * 2/ padding on 4 byte boundary
WARN("Couldn't load linked debug file %s\n", file); * 3/ CRC of the linked ELF file
*/
if (dbg_link != NO_MAP && elf_map_file(dbg_link, &fmap_link))
{
fmap_link.crc = *(const DWORD*)(dbg_link + ((DWORD_PTR)(strlen(dbg_link) + 4) & ~3));
fmap_link.with_crc = 1;
ret = elf_load_debug_info_from_map(module, &fmap_link, pool, ht_symtab);
if (!ret)
WARN("Couldn't load debug information from %s\n", dbg_link);
}
else WARN("Couldn't load linked debug file for %s\n", module->module.ModuleName);
elf_unmap_file(&fmap_link);
} }
} }
if (strstr(module->module.ModuleName, "<elf>") || if (strstr(module->module.ModuleName, "<elf>") ||
...@@ -745,10 +903,6 @@ static BOOL elf_load_debug_info_from_file( ...@@ -745,10 +903,6 @@ static BOOL elf_load_debug_info_from_file(
/* add all the public symbols from symtab */ /* add all the public symbols from symtab */
if (elf_new_public_symbols(module, ht_symtab) && !ret) ret = TRUE; if (elf_new_public_symbols(module, ht_symtab) && !ret) ret = TRUE;
leave:
if (addr != (char*)0xffffffff) munmap(addr, statbuf.st_size);
if (fd != -1) close(fd);
return ret; return ret;
} }
...@@ -757,20 +911,32 @@ leave: ...@@ -757,20 +911,32 @@ leave:
* *
* Loads ELF debugging information from the module image file. * Loads ELF debugging information from the module image file.
*/ */
BOOL elf_load_debug_info(struct module* module) BOOL elf_load_debug_info(struct module* module, struct elf_file_map* fmap)
{ {
BOOL ret; BOOL ret = TRUE;
struct pool pool; struct pool pool;
struct hash_table ht_symtab; struct hash_table ht_symtab;
struct elf_file_map my_fmap;
if (module->type != DMT_ELF || !module->elf_info)
{
ERR("Bad elf module '%s'\n", module->module.LoadedImageName);
return FALSE;
}
pool_init(&pool, 65536); pool_init(&pool, 65536);
hash_table_init(&pool, &ht_symtab, 256); hash_table_init(&pool, &ht_symtab, 256);
ret = elf_load_debug_info_from_file(module, if (!fmap)
module->module.LoadedImageName, &pool, &ht_symtab, NULL); {
fmap = &my_fmap;
ret = elf_map_file(module->module.LoadedImageName, fmap);
}
if (ret)
ret = elf_load_debug_info_from_map(module, fmap, &pool, &ht_symtab);
pool_destroy(&pool); pool_destroy(&pool);
if (fmap == &my_fmap) elf_unmap_file(fmap);
return ret; return ret;
} }
...@@ -811,63 +977,22 @@ static unsigned is_dt_flag_valid(unsigned d_tag) ...@@ -811,63 +977,22 @@ static unsigned is_dt_flag_valid(unsigned d_tag)
static BOOL elf_load_file(struct process* pcs, const char* filename, static BOOL elf_load_file(struct process* pcs, const char* filename,
unsigned long load_offset, struct elf_info* elf_info) unsigned long load_offset, struct elf_info* elf_info)
{ {
static const BYTE elf_signature[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 };
BOOL ret = FALSE; BOOL ret = FALSE;
const char* addr = (char*)0xffffffff; struct elf_file_map fmap;
int fd = -1;
struct stat statbuf;
const Elf32_Ehdr* ehptr;
const Elf32_Shdr* spnt;
const Elf32_Phdr* ppnt;
const char* shstrtab;
int i; int i;
DWORD size, start;
unsigned tmp, page_mask = getpagesize() - 1;
TRACE("Processing elf file '%s' at %08lx\n", filename, load_offset); TRACE("Processing elf file '%s' at %08lx\n", filename, load_offset);
/* check that the file exists, and that the module hasn't been loaded yet */ if (!elf_map_file(filename, &fmap)) goto leave;
if (stat(filename, &statbuf) == -1) goto leave;
/* Now open the file, so that we can mmap() it. */
if ((fd = open(filename, O_RDONLY)) == -1) goto leave;
/* Now mmap() the file. */
addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == (char*)-1) goto leave;
/* Next, we need to find a few of the internal ELF headers within /* Next, we need to find a few of the internal ELF headers within
* this thing. We need the main executable header, and the section * this thing. We need the main executable header, and the section
* table. * table.
*/ */
ehptr = (const Elf32_Ehdr*)addr; if (!fmap.elf_start && !load_offset)
if (memcmp(ehptr->e_ident, elf_signature, sizeof(elf_signature))) goto leave;
spnt = (const Elf32_Shdr*)(addr + ehptr->e_shoff);
shstrtab = (addr + spnt[ehptr->e_shstrndx].sh_offset);
/* grab size of module once loaded in memory */
ppnt = (const Elf32_Phdr*)(addr + ehptr->e_phoff);
size = 0; start = ~0L;
for (i = 0; i < ehptr->e_phnum; i++)
{
if (ppnt[i].p_type == PT_LOAD)
{
tmp = (ppnt[i].p_vaddr + ppnt[i].p_memsz + page_mask) & ~page_mask;
if (size < tmp) size = tmp;
if (ppnt[i].p_vaddr < start) start = ppnt[i].p_vaddr;
}
}
/* if non relocatable ELF, then remove fixed address from computation
* otherwise, all addresses are zero based and start has no effect
*/
size -= start;
if (!start && !load_offset)
ERR("Relocatable ELF %s, but no load address. Loading at 0x0000000\n", ERR("Relocatable ELF %s, but no load address. Loading at 0x0000000\n",
filename); filename);
if (start && load_offset) if (fmap.elf_start && load_offset)
{ {
WARN("Non-relocatable ELF %s, but load address of 0x%08lx supplied. " WARN("Non-relocatable ELF %s, but load address of 0x%08lx supplied. "
"Assuming load address is corrupt\n", filename, load_offset); "Assuming load address is corrupt\n", filename, load_offset);
...@@ -876,13 +1001,15 @@ static BOOL elf_load_file(struct process* pcs, const char* filename, ...@@ -876,13 +1001,15 @@ static BOOL elf_load_file(struct process* pcs, const char* filename,
if (elf_info->flags & ELF_INFO_DEBUG_HEADER) if (elf_info->flags & ELF_INFO_DEBUG_HEADER)
{ {
for (i = 0; i < ehptr->e_shnum; i++) const char* shstrtab = elf_map_section(&fmap, fmap.elfhdr.e_shstrndx);
if (shstrtab == NO_MAP) goto leave;
for (i = 0; i < fmap.elfhdr.e_shnum; i++)
{ {
if (strcmp(shstrtab + spnt[i].sh_name, ".dynamic") == 0 && if (strcmp(shstrtab + fmap.sect[i].shdr.sh_name, ".dynamic") == 0 &&
spnt[i].sh_type == SHT_DYNAMIC) fmap.sect[i].shdr.sh_type == SHT_DYNAMIC)
{ {
Elf32_Dyn dyn; Elf32_Dyn dyn;
char* ptr = (char*)spnt[i].sh_addr; char* ptr = (char*)fmap.sect[i].shdr.sh_addr;
unsigned long len; unsigned long len;
do do
...@@ -896,6 +1023,7 @@ static BOOL elf_load_file(struct process* pcs, const char* filename, ...@@ -896,6 +1023,7 @@ static BOOL elf_load_file(struct process* pcs, const char* filename,
elf_info->dbg_hdr_addr = dyn.d_un.d_ptr; elf_info->dbg_hdr_addr = dyn.d_un.d_ptr;
} }
} }
elf_unmap_section(&fmap, fmap.elfhdr.e_shstrndx);
} }
if (elf_info->flags & ELF_INFO_MODULE) if (elf_info->flags & ELF_INFO_MODULE)
...@@ -904,8 +1032,8 @@ static BOOL elf_load_file(struct process* pcs, const char* filename, ...@@ -904,8 +1032,8 @@ static BOOL elf_load_file(struct process* pcs, const char* filename,
HeapAlloc(GetProcessHeap(), 0, sizeof(struct elf_module_info)); HeapAlloc(GetProcessHeap(), 0, sizeof(struct elf_module_info));
if (!elf_module_info) goto leave; if (!elf_module_info) goto leave;
elf_info->module = module_new(pcs, filename, DMT_ELF, elf_info->module = module_new(pcs, filename, DMT_ELF,
(load_offset) ? load_offset : start, (load_offset) ? load_offset : fmap.elf_start,
size, 0, 0); fmap.elf_size, 0, 0);
if (!elf_info->module) if (!elf_info->module)
{ {
HeapFree(GetProcessHeap(), 0, elf_module_info); HeapFree(GetProcessHeap(), 0, elf_module_info);
...@@ -919,15 +1047,14 @@ static BOOL elf_load_file(struct process* pcs, const char* filename, ...@@ -919,15 +1047,14 @@ static BOOL elf_load_file(struct process* pcs, const char* filename,
elf_info->module->module.SymType = SymDeferred; elf_info->module->module.SymType = SymDeferred;
ret = TRUE; ret = TRUE;
} }
else ret = elf_load_debug_info(elf_info->module); else ret = elf_load_debug_info(elf_info->module, &fmap);
elf_info->module->elf_info->elf_mark = 1; elf_info->module->elf_info->elf_mark = 1;
elf_info->module->elf_info->elf_loader = 0; elf_info->module->elf_info->elf_loader = 0;
} else ret = TRUE; } else ret = TRUE;
leave: leave:
if (addr != (char*)0xffffffff) munmap((void*)addr, statbuf.st_size); elf_unmap_file(&fmap);
if (fd != -1) close(fd);
return ret; return ret;
} }
...@@ -1005,33 +1132,23 @@ static BOOL elf_search_and_load_file(struct process* pcs, const char* filename, ...@@ -1005,33 +1132,23 @@ static BOOL elf_search_and_load_file(struct process* pcs, const char* filename,
} }
/****************************************************************** /******************************************************************
* elf_synchronize_module_list * elf_enum_modules_internal
* *
* this functions rescans the debuggee module's list and synchronizes it with * Enumerate ELF modules from a running process
* the one from 'pcs', ie:
* - if a module is in debuggee and not in pcs, it's loaded into pcs
* - if a module is in pcs and not in debuggee, it's unloaded from pcs
*/ */
BOOL elf_synchronize_module_list(struct process* pcs) static BOOL elf_enum_modules_internal(const struct process* pcs,
elf_enum_modules_cb cb, void* user)
{ {
struct r_debug dbg_hdr; struct r_debug dbg_hdr;
void* lm_addr; void* lm_addr;
struct link_map lm; struct link_map lm;
char bufstr[256]; char bufstr[256];
struct elf_info elf_info;
struct module* module;
if (!pcs->dbg_hdr_addr || if (!pcs->dbg_hdr_addr ||
!ReadProcessMemory(pcs->handle, (void*)pcs->dbg_hdr_addr, !ReadProcessMemory(pcs->handle, (void*)pcs->dbg_hdr_addr,
&dbg_hdr, sizeof(dbg_hdr), NULL)) &dbg_hdr, sizeof(dbg_hdr), NULL))
return FALSE; return FALSE;
for (module = pcs->lmodules; module; module = module->next)
{
if (module->type == DMT_ELF) module->elf_info->elf_mark = 0;
}
elf_info.flags = ELF_INFO_MODULE;
/* Now walk the linked list. In all known ELF implementations, /* Now walk the linked list. In all known ELF implementations,
* the dynamic loader maintains this linked list for us. In some * the dynamic loader maintains this linked list for us. In some
* cases the first entry doesn't appear with a name, in other cases it * cases the first entry doesn't appear with a name, in other cases it
...@@ -1047,13 +1164,52 @@ BOOL elf_synchronize_module_list(struct process* pcs) ...@@ -1047,13 +1164,52 @@ BOOL elf_synchronize_module_list(struct process* pcs)
ReadProcessMemory(pcs->handle, lm.l_name, bufstr, sizeof(bufstr), NULL)) ReadProcessMemory(pcs->handle, lm.l_name, bufstr, sizeof(bufstr), NULL))
{ {
bufstr[sizeof(bufstr) - 1] = '\0'; bufstr[sizeof(bufstr) - 1] = '\0';
elf_search_and_load_file(pcs, bufstr, (unsigned long)lm.l_addr, if (!cb(bufstr, lm.l_addr, user)) break;
&elf_info);
} }
} }
return TRUE;
}
struct elf_sync
{
struct process* pcs;
struct elf_info elf_info;
};
static BOOL elf_enum_sync_cb(const char* name, unsigned long addr, void* user)
{
struct elf_sync* es = user;
elf_search_and_load_file(es->pcs, name, addr, &es->elf_info);
return TRUE;
}
/******************************************************************
* elf_synchronize_module_list
*
* this functions rescans the debuggee module's list and synchronizes it with
* the one from 'pcs', ie:
* - if a module is in debuggee and not in pcs, it's loaded into pcs
* - if a module is in pcs and not in debuggee, it's unloaded from pcs
*/
BOOL elf_synchronize_module_list(struct process* pcs)
{
struct module* module;
struct elf_sync es;
for (module = pcs->lmodules; module; module = module->next) for (module = pcs->lmodules; module; module = module->next)
{ {
if (module->type == DMT_ELF) module->elf_info->elf_mark = 0;
}
es.pcs = pcs;
es.elf_info.flags = ELF_INFO_MODULE;
if (!elf_enum_modules_internal(pcs, elf_enum_sync_cb, &es))
return FALSE;
module = pcs->lmodules;
while (module)
{
if (module->type == DMT_ELF && !module->elf_info->elf_mark && if (module->type == DMT_ELF && !module->elf_info->elf_mark &&
!module->elf_info->elf_loader) !module->elf_info->elf_loader)
{ {
...@@ -1061,97 +1217,141 @@ BOOL elf_synchronize_module_list(struct process* pcs) ...@@ -1061,97 +1217,141 @@ BOOL elf_synchronize_module_list(struct process* pcs)
/* restart all over */ /* restart all over */
module = pcs->lmodules; module = pcs->lmodules;
} }
else module = module->next;
} }
return TRUE; return TRUE;
} }
/****************************************************************** /******************************************************************
* elf_read_wine_loader_dbg_info * elf_search_loader
* *
* Try to find a decent wine executable which could have loaded the debuggee * Lookup in a running ELF process the loader, and sets its ELF link
* address (for accessing the list of loaded .so libs) in pcs.
* If flags is ELF_INFO_MODULE, the module for the loader is also
* added as a module into pcs.
*/ */
BOOL elf_read_wine_loader_dbg_info(struct process* pcs) static BOOL elf_search_loader(struct process* pcs, struct elf_info* elf_info)
{ {
const char* ptr;
struct elf_info elf_info;
BOOL ret; BOOL ret;
const char* ptr;
elf_info.flags = ELF_INFO_DEBUG_HEADER | ELF_INFO_MODULE;
/* All binaries are loaded with WINELOADER (if run from tree) or by the /* All binaries are loaded with WINELOADER (if run from tree) or by the
* main executable (either wine-kthread or wine-pthread) * main executable (either wine-kthread or wine-pthread)
* Note: the heuristic use to know whether we need to load wine-pthread or * FIXME: the heuristic used to know whether we need to load wine-pthread
* wine-kthread is not 100% safe * or wine-kthread is not 100% safe
*/ */
if ((ptr = getenv("WINELOADER"))) if ((ptr = getenv("WINELOADER")))
ret = elf_search_and_load_file(pcs, ptr, 0, &elf_info); ret = elf_search_and_load_file(pcs, ptr, 0, elf_info);
else else
{ {
ret = elf_search_and_load_file(pcs, "wine-kthread", 0, &elf_info) || ret = elf_search_and_load_file(pcs, "wine-kthread", 0, elf_info) ||
elf_search_and_load_file(pcs, "wine-pthread", 0, &elf_info); elf_search_and_load_file(pcs, "wine-pthread", 0, elf_info);
} }
if (!ret) return FALSE; return ret;
}
/******************************************************************
* elf_read_wine_loader_dbg_info
*
* Try to find a decent wine executable which could have loaded the debuggee
*/
BOOL elf_read_wine_loader_dbg_info(struct process* pcs)
{
struct elf_info elf_info;
elf_info.flags = ELF_INFO_DEBUG_HEADER | ELF_INFO_MODULE;
if (!elf_search_loader(pcs, &elf_info)) return FALSE;
elf_info.module->elf_info->elf_loader = 1; elf_info.module->elf_info->elf_loader = 1;
strcpy(elf_info.module->module.ModuleName, "<wine-loader>"); strcpy(elf_info.module->module.ModuleName, "<wine-loader>");
return (pcs->dbg_hdr_addr = elf_info.dbg_hdr_addr) != 0; return (pcs->dbg_hdr_addr = elf_info.dbg_hdr_addr) != 0;
} }
/****************************************************************** /******************************************************************
* elf_enum_modules
*
* Enumerates the ELF loaded modules from a running target (hProc)
* This function doesn't require that someone has called SymInitialize
* on this very process.
*/
BOOL elf_enum_modules(HANDLE hProc, elf_enum_modules_cb cb, void* user)
{
struct process pcs;
struct elf_info elf_info;
pcs.handle = hProc;
elf_info.flags = ELF_INFO_DEBUG_HEADER;
if (!elf_search_loader(&pcs, &elf_info)) return FALSE;
return elf_enum_modules_internal(&pcs, cb, user);
}
struct elf_load
{
struct process* pcs;
struct elf_info elf_info;
const char* name;
BOOL ret;
};
/******************************************************************
* elf_load_cb
*
* Callback for elf_load_module, used to walk the list of loaded
* modules.
*/
static BOOL elf_load_cb(const char* name, unsigned long addr, void* user)
{
struct elf_load* el = user;
const char* p;
/* memcmp is needed for matches when bufstr contains also version information
* el->name: libc.so, name: libc.so.6.0
*/
p = strrchr(name, '/');
if (!p++) p = name;
if (!memcmp(p, el->name, strlen(el->name)))
{
elf_search_and_load_file(el->pcs, name, addr, &el->elf_info);
return FALSE;
}
return TRUE;
}
/******************************************************************
* elf_load_module * elf_load_module
* *
* loads an ELF module and stores it in process' module list * loads an ELF module and stores it in process' module list
* Also, find module real name and load address from * Also, find module real name and load address from
* the real loaded modules list in pcs address space * the real loaded modules list in pcs address space
*/ */
struct module* elf_load_module(struct process* pcs, const char* name) struct module* elf_load_module(struct process* pcs, const char* name, DWORD addr)
{ {
struct elf_info elf_info; struct elf_load el;
BOOL ret = FALSE;
const char* p;
const char* xname;
struct r_debug dbg_hdr;
void* lm_addr;
struct link_map lm;
char bufstr[256];
TRACE("(%p %s)\n", pcs, name); TRACE("(%p %s)\n", pcs, name);
elf_info.flags = ELF_INFO_MODULE; el.elf_info.flags = ELF_INFO_MODULE;
el.ret = FALSE;
/* do only the lookup from the filename, not the path (as we lookup module name
* in the process' loaded module list)
*/
xname = strrchr(name, '/');
if (!xname++) xname = name;
if (!ReadProcessMemory(pcs->handle, (void*)pcs->dbg_hdr_addr, &dbg_hdr, sizeof(dbg_hdr), NULL))
return NULL;
for (lm_addr = (void*)dbg_hdr.r_map; lm_addr; lm_addr = (void*)lm.l_next) if (pcs->dbg_hdr_addr) /* we're debugging a life target */
{ {
if (!ReadProcessMemory(pcs->handle, lm_addr, &lm, sizeof(lm), NULL)) el.pcs = pcs;
return NULL; /* do only the lookup from the filename, not the path (as we lookup module name
* in the process' loaded module list)
*/
el.name = strrchr(name, '/');
if (!el.name++) el.name = name;
el.ret = FALSE;
if (lm.l_prev != NULL && /* skip first entry, normally debuggee itself */ if (!elf_enum_modules_internal(pcs, elf_load_cb, &el))
lm.l_name != NULL && return NULL;
ReadProcessMemory(pcs->handle, lm.l_name, bufstr, sizeof(bufstr), NULL)) }
{ else if (addr)
bufstr[sizeof(bufstr) - 1] = '\0'; {
/* memcmp is needed for matches when bufstr contains also version information el.ret = elf_search_and_load_file(pcs, name, addr, &el.elf_info);
* name: libc.so, bufstr: libc.so.6.0
*/
p = strrchr(bufstr, '/');
if (!p++) p = bufstr;
if (!memcmp(p, xname, strlen(xname)))
{
ret = elf_search_and_load_file(pcs, bufstr,
(unsigned long)lm.l_addr, &elf_info);
break;
}
}
} }
if (!lm_addr || !ret) return NULL; if (!el.ret) return NULL;
assert(elf_info.module); assert(el.elf_info.module);
return elf_info.module; return el.elf_info.module;
} }
#else /* !__ELF__ */ #else /* !__ELF__ */
...@@ -1166,7 +1366,7 @@ BOOL elf_read_wine_loader_dbg_info(struct process* pcs) ...@@ -1166,7 +1366,7 @@ BOOL elf_read_wine_loader_dbg_info(struct process* pcs)
return FALSE; return FALSE;
} }
struct module* elf_load_module(struct process* pcs, const char* name) struct module* elf_load_module(struct process* pcs, const char* name, DWORD addr)
{ {
return NULL; return NULL;
} }
......
...@@ -48,6 +48,10 @@ static void module_fill_module(const char* in, char* out, unsigned size) ...@@ -48,6 +48,10 @@ static void module_fill_module(const char* in, char* out, unsigned size)
if (len > 4 && if (len > 4 &&
(!strcasecmp(&out[len - 4], ".dll") || !strcasecmp(&out[len - 4], ".exe"))) (!strcasecmp(&out[len - 4], ".dll") || !strcasecmp(&out[len - 4], ".exe")))
out[len - 4] = '\0'; out[len - 4] = '\0';
else if (((len > 12 && out[len - 13] == '/') || len == 12) &&
(!strcasecmp(out + len - 12, "wine-pthread") ||
!strcasecmp(out + len - 12, "wine-kthread")))
strcpy(out, "<wine-loader>");
else else
{ {
if (len > 7 && if (len > 7 &&
...@@ -134,14 +138,17 @@ struct module* module_find_by_name(const struct process* pcs, ...@@ -134,14 +138,17 @@ struct module* module_find_by_name(const struct process* pcs,
} }
else else
{ {
char modname[MAX_PATH];
for (module = pcs->lmodules; module; module = module->next) for (module = pcs->lmodules; module; module = module->next)
{ {
if (type == module->type && !strcasecmp(name, module->module.LoadedImageName)) if (type == module->type && !strcasecmp(name, module->module.LoadedImageName))
return module; return module;
} }
module_fill_module(name, modname, sizeof(modname));
for (module = pcs->lmodules; module; module = module->next) for (module = pcs->lmodules; module; module = module->next)
{ {
if (type == module->type && !strcasecmp(name, module->module.ModuleName)) if (type == module->type && !strcasecmp(modname, module->module.ModuleName))
return module; return module;
} }
} }
...@@ -214,9 +221,9 @@ struct module* module_get_debug(const struct process* pcs, struct module* module ...@@ -214,9 +221,9 @@ struct module* module_get_debug(const struct process* pcs, struct module* module
switch (module->type) switch (module->type)
{ {
case DMT_ELF: ret = elf_load_debug_info(module); break; case DMT_ELF: ret = elf_load_debug_info(module, NULL); break;
case DMT_PE: ret = pe_load_debug_info(pcs, module); break; case DMT_PE: ret = pe_load_debug_info(pcs, module); break;
default: ret = FALSE; break; default: ret = FALSE; break;
} }
if (!ret) module->module.SymType = SymNone; if (!ret) module->module.SymType = SymNone;
assert(module->module.SymType != SymDeferred); assert(module->module.SymType != SymDeferred);
...@@ -277,6 +284,33 @@ static BOOL module_is_elf_container_loaded(struct process* pcs, const char* Imag ...@@ -277,6 +284,33 @@ static BOOL module_is_elf_container_loaded(struct process* pcs, const char* Imag
return FALSE; return FALSE;
} }
static BOOL elf_is_shared_by_name(const char* name)
{
const char* ptr;
int len = strlen(name);
/* check for terminating .so or .so.[digit]+ */
while (len)
{
for (ptr = name + len - 1; ptr >= name; ptr--) if (*ptr == '.') break;
if (ptr < name) break;
if (ptr == name + len - 2 && isdigit(ptr[1]))
{
len -= 2;
continue;
}
if (ptr == name + len - 3 && ptr[1] == 's' && ptr[2] == 'o')
return TRUE;
break;
}
/* wine-[kp]thread is valid too */
if (((len > 12 && name[len - 13] == '/') || len == 12) &&
(!strcasecmp(name + len - 12, "wine-pthread") ||
!strcasecmp(name + len - 12, "wine-kthread")))
return TRUE;
return FALSE;
}
/*********************************************************************** /***********************************************************************
* SymLoadModule (DBGHELP.@) * SymLoadModule (DBGHELP.@)
*/ */
...@@ -310,11 +344,11 @@ DWORD WINAPI SymLoadModule(HANDLE hProcess, HANDLE hFile, char* ImageName, ...@@ -310,11 +344,11 @@ DWORD WINAPI SymLoadModule(HANDLE hProcess, HANDLE hFile, char* ImageName,
TRACE("Assuming %s as native DLL\n", ImageName); TRACE("Assuming %s as native DLL\n", ImageName);
if (!(module = pe_load_module(pcs, ImageName, hFile, BaseOfDll, SizeOfDll))) if (!(module = pe_load_module(pcs, ImageName, hFile, BaseOfDll, SizeOfDll)))
{ {
unsigned len = strlen(ImageName); if (elf_is_shared_by_name(ImageName) &&
(module = elf_load_module(pcs, ImageName, BaseOfDll)))
if (!strcmp(ImageName + len - 3, ".so") && goto done;
(module = elf_load_module(pcs, ImageName))) goto done; FIXME("Should have successfully loaded debug information for image %s\n",
FIXME("should have successfully loaded some debug information for image %s\n", ImageName); ImageName);
if ((module = pe_load_module_from_pcs(pcs, ImageName, ModuleName, BaseOfDll, SizeOfDll))) if ((module = pe_load_module_from_pcs(pcs, ImageName, ModuleName, BaseOfDll, SizeOfDll)))
goto done; goto done;
WARN("Couldn't locate %s\n", ImageName); WARN("Couldn't locate %s\n", ImageName);
...@@ -418,7 +452,7 @@ BOOL WINAPI EnumerateLoadedModules(HANDLE hProcess, ...@@ -418,7 +452,7 @@ BOOL WINAPI EnumerateLoadedModules(HANDLE hProcess,
DWORD i, sz; DWORD i, sz;
MODULEINFO mi; MODULEINFO mi;
hMods = HeapAlloc(GetProcessHeap(), 0, sz); hMods = HeapAlloc(GetProcessHeap(), 0, 256 * sizeof(hMods[0]));
if (!hMods) return FALSE; if (!hMods) return FALSE;
if (!EnumProcessModules(hProcess, hMods, 256 * sizeof(hMods[0]), &sz)) if (!EnumProcessModules(hProcess, hMods, 256 * sizeof(hMods[0]), &sz))
......
...@@ -404,21 +404,25 @@ struct module* pe_load_module_from_pcs(struct process* pcs, const char* name, ...@@ -404,21 +404,25 @@ struct module* pe_load_module_from_pcs(struct process* pcs, const char* name,
} }
} }
if (ptr && (module = module_find_by_name(pcs, ptr, DMT_PE))) return module; if (ptr && (module = module_find_by_name(pcs, ptr, DMT_PE))) return module;
if (base && pcs->dbg_hdr_addr) if (base)
{ {
IMAGE_DOS_HEADER dos; if (pcs->dbg_hdr_addr)
IMAGE_NT_HEADERS nth;
if (ReadProcessMemory(pcs->handle, (char*)base, &dos, sizeof(dos), NULL) &&
dos.e_magic == IMAGE_DOS_SIGNATURE &&
ReadProcessMemory(pcs->handle, (char*)(base + dos.e_lfanew),
&nth, sizeof(nth), NULL) &&
nth.Signature == IMAGE_NT_SIGNATURE)
{ {
if (!size) size = nth.OptionalHeader.SizeOfImage; IMAGE_DOS_HEADER dos;
module = module_new(pcs, name, DMT_PE, base, size, IMAGE_NT_HEADERS nth;
nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum);
} if (ReadProcessMemory(pcs->handle, (char*)base, &dos, sizeof(dos), NULL) &&
dos.e_magic == IMAGE_DOS_SIGNATURE &&
ReadProcessMemory(pcs->handle, (char*)(base + dos.e_lfanew),
&nth, sizeof(nth), NULL) &&
nth.Signature == IMAGE_NT_SIGNATURE)
{
if (!size) size = nth.OptionalHeader.SizeOfImage;
module = module_new(pcs, name, DMT_PE, base, size,
nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum);
}
} else if (size)
module = module_new(pcs, name, DMT_PE, base, size, 0 /* FIXME */, 0 /* FIXME */);
} }
return module; return module;
} }
...@@ -100,6 +100,7 @@ struct stab_nlist ...@@ -100,6 +100,7 @@ struct stab_nlist
static void stab_strcpy(char* dest, int sz, const char* source) static void stab_strcpy(char* dest, int sz, const char* source)
{ {
char* ptr = dest;
/* /*
* A strcpy routine that stops when we hit the ':' character. * A strcpy routine that stops when we hit the ':' character.
* Faster than copying the whole thing, and then nuking the * Faster than copying the whole thing, and then nuking the
...@@ -108,28 +109,27 @@ static void stab_strcpy(char* dest, int sz, const char* source) ...@@ -108,28 +109,27 @@ static void stab_strcpy(char* dest, int sz, const char* source)
*/ */
while (*source != '\0') while (*source != '\0')
{ {
if (source[0] != ':' && sz-- > 0) *dest++ = *source++; if (source[0] != ':' && sz-- > 0) *ptr++ = *source++;
else if (source[1] == ':' && (sz -= 2) > 0) else if (source[1] == ':' && (sz -= 2) > 0)
{ {
*dest++ = *source++; *ptr++ = *source++;
*dest++ = *source++; *ptr++ = *source++;
} }
else break; else break;
} }
*dest-- = '\0'; *ptr-- = '\0';
/* GCC seems to emit, in some cases, a .<digit>+ suffix. /* GCC emits, in some cases, a .<digit>+ suffix.
* This is used for static variable inside functions, so * This is used for static variable inside functions, so
* that we can have several such variables with same name in * that we can have several such variables with same name in
* the same compilation unit * the same compilation unit
* We simply ignore that suffix when present (we also get rid * We simply ignore that suffix when present (we also get rid
* of it in ELF symtab parsing) * of it in ELF symtab parsing)
*/ */
if (isdigit(*dest)) if (ptr >= dest && isdigit(*ptr))
{ {
while (isdigit(*dest)) dest--; while (ptr > dest && isdigit(*ptr)) ptr--;
if (*dest == '.') *dest = '\0'; if (*ptr == '.') *ptr = '\0';
} }
assert(sz > 0); assert(sz > 0);
} }
...@@ -1098,7 +1098,7 @@ struct pending_loc_var ...@@ -1098,7 +1098,7 @@ struct pending_loc_var
* function (assuming that current function ends where next function starts) * function (assuming that current function ends where next function starts)
*/ */
static void stabs_finalize_function(struct module* module, struct symt_function* func, static void stabs_finalize_function(struct module* module, struct symt_function* func,
unsigned long end) unsigned long size)
{ {
IMAGEHLP_LINE il; IMAGEHLP_LINE il;
...@@ -1113,7 +1113,7 @@ static void stabs_finalize_function(struct module* module, struct symt_function* ...@@ -1113,7 +1113,7 @@ static void stabs_finalize_function(struct module* module, struct symt_function*
symt_add_function_point(module, func, SymTagFuncDebugStart, symt_add_function_point(module, func, SymTagFuncDebugStart,
il.Address - func->address, NULL); il.Address - func->address, NULL);
} }
if (end) func->size = end - func->address; if (size) func->size = size;
} }
BOOL stabs_parse(struct module* module, unsigned long load_offset, BOOL stabs_parse(struct module* module, unsigned long load_offset,
...@@ -1389,10 +1389,6 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset, ...@@ -1389,10 +1389,6 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset,
} }
break; break;
case N_FUN: case N_FUN:
/* First, clean up the previous function we were working on. */
stabs_finalize_function(module, curr_func,
stab_ptr->n_value ? load_offset + stab_ptr->n_value : 0);
/* /*
* For now, just declare the various functions. Later * For now, just declare the various functions. Later
* on, we will add the line number information and the * on, we will add the line number information and the
...@@ -1409,6 +1405,17 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset, ...@@ -1409,6 +1405,17 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset,
if (*symname) if (*symname)
{ {
struct symt_function_signature* func_type; struct symt_function_signature* func_type;
if (curr_func)
{
/* First, clean up the previous function we were working on.
* Assume size of the func is the delta between current offset
* and offset of last function
*/
stabs_finalize_function(module, curr_func,
stab_ptr->n_value ?
(load_offset + stab_ptr->n_value - curr_func->address) : 0);
}
func_type = symt_new_function_signature(module, func_type = symt_new_function_signature(module,
stabs_parse_type(ptr)); stabs_parse_type(ptr));
curr_func = symt_new_function(module, compiland, symname, curr_func = symt_new_function(module, compiland, symname,
...@@ -1417,7 +1424,10 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset, ...@@ -1417,7 +1424,10 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset,
} }
else else
{ {
/* some GCC seem to use a N_FUN "" to mark the end of a function */ /* some versions of GCC to use a N_FUN "" to mark the end of a function
* and n_value contains the size of the func
*/
stabs_finalize_function(module, curr_func, stab_ptr->n_value);
curr_func = NULL; curr_func = NULL;
} }
break; break;
...@@ -1430,8 +1440,7 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset, ...@@ -1430,8 +1440,7 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset,
{ {
/* Nuke old path. */ /* Nuke old path. */
srcpath[0] = '\0'; srcpath[0] = '\0';
stabs_finalize_function(module, curr_func, stabs_finalize_function(module, curr_func, 0);
stab_ptr->n_value ? load_offset + stab_ptr->n_value : 0);
curr_func = NULL; curr_func = NULL;
source_idx = -1; source_idx = -1;
incl_stk = -1; incl_stk = -1;
...@@ -1467,9 +1476,12 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset, ...@@ -1467,9 +1476,12 @@ BOOL stabs_parse(struct module* module, unsigned long load_offset,
strs += strtabinc; strs += strtabinc;
strtabinc = stab_ptr->n_value; strtabinc = stab_ptr->n_value;
/* I'm not sure this is needed, so trace it before we obsolete it */ /* I'm not sure this is needed, so trace it before we obsolete it */
if (curr_func) FIXME("UNDF: curr_func %s\n", curr_func->hash_elt.name); if (curr_func)
stabs_finalize_function(module, curr_func, 0); /* FIXME */ {
curr_func = NULL; FIXME("UNDF: curr_func %s\n", curr_func->hash_elt.name);
stabs_finalize_function(module, curr_func, 0); /* FIXME */
curr_func = NULL;
}
break; break;
case N_OPT: case N_OPT:
/* Ignore this. We don't care what it points to. */ /* Ignore this. We don't care what it points to. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment