From 116377f5e3420df5c5db7951cb829290a2a6571d Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Fri, 23 Jun 2017 07:40:02 +0800 Subject: [PATCH] Fix function base address lookup on Win32 and FreeBSD * Add a (slow but cross-platform) fallback lookup method of function name and base address using LLVM debug info reader * Remove windows exported symbol lookup that never worked for sysimg function address lookup (since they are never exported) Fix #17251 Fix #20798 --- src/debuginfo.cpp | 90 +++++++++++++++++++++++++-------------------- test/cmdlineargs.jl | 8 +--- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 7e96f3c908fec0..5f19a18afb6a90 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -746,35 +746,12 @@ bool jl_dylib_DI_for_fptr(size_t pointer, const llvm::object::ObjectFile **obj, if (onlySysImg && !insysimage) { return false; } - static char frame_info_func[ - sizeof(SYMBOL_INFO) + - MAX_SYM_NAME * sizeof(TCHAR)]; - DWORD64 dwDisplacement64 = 0; - DWORD64 dwAddress = pointer; - PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)frame_info_func; - pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); - pSymbol->MaxNameLen = MAX_SYM_NAME; - jl_in_stackwalk = 1; - if (SymFromAddr(GetCurrentProcess(), dwAddress, &dwDisplacement64, - pSymbol)) { - // SymFromAddr returned success - // errors are ignored, but are hopefully patched up by - // using llvm to read the object (below) - if (name) - jl_copy_str(name, pSymbol->Name); - if (saddr) - *saddr = (void*)(uintptr_t)pSymbol->Address; - } - else if (saddr) { - *saddr = NULL; - } - // If we didn't find the filename before in the debug // info, use the dll name if (filename && !*filename) jl_copy_str(filename, fname.data()); - - jl_in_stackwalk = 0; + if (saddr) + *saddr = NULL; #else // ifdef _OS_WINDOWS_ Dl_info dlinfo; @@ -998,33 +975,66 @@ static int jl_getDylibFunctionInfo(jl_frame_t **frames, size_t pointer, int skip frame0->fromC = 1; return 1; } + bool need_saddr = isSysImg && sysimg_fvars; frame0->fromC = !isSysImg; - if (isSysImg && sysimg_fvars) { + // Try platform specific methods first since they are usually faster + if (need_saddr && !saddr) { #if defined(_OS_LINUX_) && !defined(JL_DISABLE_LIBUNWIND) unw_proc_info_t pip; - if (!saddr && unw_get_proc_info_by_ip(unw_local_addr_space, - pointer, &pip, NULL) == 0) + if (unw_get_proc_info_by_ip(unw_local_addr_space, pointer, &pip, NULL) == 0) { saddr = (void*)pip.start_ip; + } #endif #if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) - if (!saddr) { - DWORD64 ImageBase; - PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry(pointer, &ImageBase, NULL); - if (fn) - saddr = (void*)(ImageBase + fn->BeginAddress); + DWORD64 ImageBase; + PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry(pointer, &ImageBase, NULL); + if (fn) { + saddr = (void*)(ImageBase + fn->BeginAddress); } #endif - if (saddr) { - for (size_t i = 0; i < sysimg_fvars_n; i++) { - if (saddr == sysimg_fvars[i]) { - frame0->linfo = sysimg_fvars_linfo[i]; - break; + } + if (((need_saddr && !saddr) || !frame0->func_name) && object) { + size_t distance = (size_t)-1; + SymRef sym_found; + for (auto sym: object->symbols()) { + auto addr = sym.getAddress(); + if (!addr) + continue; + size_t symptr = addr.get(); + if (symptr > pointer + slide) + continue; + size_t new_dist = pointer + slide - symptr; + if (new_dist > distance) + continue; + distance = new_dist; + sym_found = sym; + } + if (distance != (size_t)-1) { + if (!saddr) { + auto addr = sym_found.getAddress(); + assert(addr); + saddr = (void*)(uintptr_t)addr.get(); + } + if (!frame0->func_name) { + if (auto name_or_err = sym_found.getName()) { + auto name = name_or_err.get(); + size_t len = name.size(); + frame0->func_name = (char*)malloc(len + 1); + frame0->func_name[len] = 0; + memcpy(frame0->func_name, name.data(), len); } } } - return lookup_pointer(context, frames, pointer+slide, isSysImg, noInline); } - return lookup_pointer(context, frames, pointer+slide, isSysImg, noInline); + if (need_saddr && saddr) { + for (size_t i = 0; i < sysimg_fvars_n; i++) { + if (saddr == sysimg_fvars[i]) { + frame0->linfo = sysimg_fvars_linfo[i]; + break; + } + } + } + return lookup_pointer(context, frames, pointer + slide, isSysImg, noInline); } int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide, int64_t *section_slide, diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 41544f87f5df95..fc54c1781bd0e6 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -423,13 +423,7 @@ end for precomp in ("yes", "no") bt = readstring(pipeline(ignorestatus(`$(Base.julia_cmd()) --startup-file=no --precompiled=$precomp -E 'include("____nonexistent_file")'`), stderr=catcmd)) - @test contains(bt, "include_from_node1") - if ((is_windows() && Sys.WORD_SIZE == 32) || (is_bsd() && !is_apple())) && precomp == "yes" - # FIXME: Issue #17251 (Windows), #20798 (FreeBSD) - @test_broken contains(bt, "include_from_node1(::Module, ::String) at $(joinpath(".", "loading.jl"))") - else - @test contains(bt, "include_from_node1(::Module, ::String) at $(joinpath(".", "loading.jl"))") - end + @test contains(bt, "include_from_node1(::Module, ::String) at $(joinpath(".", "loading.jl"))") lno = match(r"at \.[\/\\]loading\.jl:(\d+)", bt) @test length(lno.captures) == 1 @test parse(Int, lno.captures[1]) > 0