Skip to content

Commit

Permalink
Proxy calls to dl (#1686)
Browse files Browse the repository at this point in the history
* Proxy calls to dl

Some Linux distros don't have a version of libdl.so without a version prefix. Call from manage code will fail to
find the library on these distros. Proxying via a C++ library should cure this, because the C libraries will
handle loading in this case.

* Add linker command for libdil

* Rename exported fuctions to avoid strange issue

* Rewrite PInvoke tables for the native methods used by App Sec too

* Correct build error on windows

Co-authored-by: Robert Pickering <robertfpickering@fastmail.com>
  • Loading branch information
robertpi and Robert Pickering authored Aug 19, 2021
1 parent 13c62ee commit 5080043
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 105 deletions.
17 changes: 12 additions & 5 deletions build/_build/Build.Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,24 +118,31 @@ partial class Build
.Description("Builds and runs a sample app using dotnet run, enabling profiling.")
.Requires(() => SampleName)
.Requires(() => Framework)
.Requires(() => IsWin)
.Executes(() =>
{
var envVars = new Dictionary<string,string>()
{
{"COR_ENABLE_PROFILING", "1"},
{"COR_PROFILER", "{846F5F1C-F9AE-4B07-969E-05C26BC060D8}"},
{"COR_PROFILER_PATH_32", TracerHomeDirectory / "win-x86" / "Datadog.Trace.ClrProfiler.Native.dll" },
{"COR_PROFILER_PATH_64", TracerHomeDirectory / "win-x64" / "Datadog.Trace.ClrProfiler.Native.dll" },
{"COR_PROFILER_PATH_32", TracerHomeDirectory / "win-x86" / "Datadog.Trace.ClrProfiler.Native.dll"},
{"COR_PROFILER_PATH_64", TracerHomeDirectory / "win-x64" / "Datadog.Trace.ClrProfiler.Native.dll"},
{"CORECLR_ENABLE_PROFILING", "1"},
{"CORECLR_PROFILER", "{846F5F1C-F9AE-4B07-969E-05C26BC060D8}"},
{"CORECLR_PROFILER_PATH_32", TracerHomeDirectory / "win-x86" / "Datadog.Trace.ClrProfiler.Native.dll" },
{"CORECLR_PROFILER_PATH_64", TracerHomeDirectory / "win-x64" / "Datadog.Trace.ClrProfiler.Native.dll" },
{"DD_INTEGRATIONS", TracerHomeDirectory / "integrations.json" },
{"DD_DOTNET_TRACER_HOME", TracerHomeDirectory },
{"ASPNETCORE_URLS", "https://*:5003" },
};
if (IsWin)
{
envVars.Add("CORECLR_PROFILER_PATH_32", TracerHomeDirectory / "win-x86" / "Datadog.Trace.ClrProfiler.Native.dll");
envVars.Add("CORECLR_PROFILER_PATH_64", TracerHomeDirectory / "win-x64" / "Datadog.Trace.ClrProfiler.Native.dll");
}
else
{
envVars.Add("CORECLR_PROFILER_PATH", TracerHomeDirectory / "Datadog.Trace.ClrProfiler.Native.so");
}
if (ExtraEnvVars?.Length > 0)
{
foreach (var envVar in ExtraEnvVars)
Expand Down
2 changes: 2 additions & 0 deletions build_in_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ docker run -it --rm \
--env NugetPackageDirectory=/project/packages \
--env tracerHome=/project/bin/tracer-home \
--env artifacts=/project/bin/artifacts \
-p 5003:5003 \
-v /ddlogs:/var/log/datadog/dotnet \
$IMAGE_NAME \
dotnet /build/bin/Debug/_build.dll "$@"
2 changes: 2 additions & 0 deletions src/Datadog.Trace.ClrProfiler.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,13 @@ if (ISMACOS)
target_link_libraries("Datadog.Trace.ClrProfiler.Native.static"
${OUTPUT_DEPS_DIR}/re2/obj/libre2.a
${OUTPUT_DEPS_DIR}/fmt/libfmt.a
${CMAKE_DL_LIBS}
)
elseif(ISLINUX)
target_link_libraries("Datadog.Trace.ClrProfiler.Native.static"
${OUTPUT_DEPS_DIR}/re2/obj/libre2.a
${OUTPUT_DEPS_DIR}/fmt/libfmt.a
${CMAKE_DL_LIBS}
-static-libgcc
-static-libstdc++
)
Expand Down
167 changes: 90 additions & 77 deletions src/Datadog.Trace.ClrProfiler.Native/cor_profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,93 @@ HRESULT STDMETHODCALLTYPE CorProfiler::AssemblyLoadFinished(AssemblyID assembly_
return S_OK;
}

void CorProfiler::RewritingPInvokeMaps(ComPtr<IUnknown> metadata_interfaces, ModuleMetadata* module_metadata, WSTRING nativemethods_type_name)
{
HRESULT hr;
const auto metadata_import = metadata_interfaces.As<IMetaDataImport2>(IID_IMetaDataImport);
const auto metadata_emit = metadata_interfaces.As<IMetaDataEmit2>(IID_IMetaDataEmit);

// We are in the right module, so we try to load the mdTypeDef from the target type name.
mdTypeDef nativeMethodsTypeDef = mdTypeDefNil;
auto foundType = FindTypeDefByName(nativemethods_type_name,
module_metadata->assemblyName, metadata_import, nativeMethodsTypeDef);
if (foundType)
{
// Define the actual profiler file path as a ModuleRef
WSTRING native_profiler_file = GetEnvironmentValue(environment::internal_trace_profiler_path);
if (native_profiler_file.empty())
{
native_profiler_file = GetCoreCLRProfilerPath();
}
mdModuleRef profiler_ref;
hr = metadata_emit->DefineModuleRef(native_profiler_file.c_str(), &profiler_ref);
if (SUCCEEDED(hr))
{
// Enumerate all methods inside the native methods type with the PInvokes
Enumerator<mdMethodDef> enumMethods = Enumerator<mdMethodDef>(
[metadata_import, nativeMethodsTypeDef](HCORENUM* ptr, mdMethodDef arr[], ULONG max, ULONG* cnt) -> HRESULT
{
return metadata_import->EnumMethods(ptr, nativeMethodsTypeDef, arr, max, cnt);
}, [metadata_import](HCORENUM ptr) -> void { metadata_import->CloseEnum(ptr); });

EnumeratorIterator<mdMethodDef> enumIterator = enumMethods.begin();
while (enumIterator != enumMethods.end())
{
auto methodDef = *enumIterator;

const auto caller = GetFunctionInfo(module_metadata->metadata_import, methodDef);
Logger::Info("Rewriting pinvoke for: ", caller.name);

// Get the current PInvoke map to extract the flags and the entrypoint name
DWORD pdwMappingFlags;
WCHAR importName[kNameMaxSize]{};
DWORD importNameLength = 0;
mdModuleRef importModule;
hr = metadata_import->GetPinvokeMap(methodDef, &pdwMappingFlags, importName, kNameMaxSize,
&importNameLength, &importModule);
if (SUCCEEDED(hr))
{
// Delete the current PInvoke map
hr = metadata_emit->DeletePinvokeMap(methodDef);
if (SUCCEEDED(hr))
{
// Define a new PInvoke map with the new ModuleRef of the actual profiler file path
hr = metadata_emit->DefinePinvokeMap(methodDef, pdwMappingFlags,
WSTRING(importName).c_str(), profiler_ref);
if (FAILED(hr))
{
Logger::Warn("ModuleLoadFinished: DefinePinvokeMap to the actual profiler file path "
"failed, trying to restore the previous one.");
hr = metadata_emit->DefinePinvokeMap(methodDef, pdwMappingFlags,
WSTRING(importName).c_str(), importModule);
if (FAILED(hr))
{
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: Error trying to restore the previous PInvokeMap.");
}
}
}
else
{
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: DeletePinvokeMap failed");
}
}

enumIterator = ++enumIterator;
}
}
else
{
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: Native Profiler DefineModuleRef failed");
}
}
}

HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadFinished(ModuleID module_id, HRESULT hr_status)
{
auto _ = trace::Stats::Instance()->ModuleLoadFinishedMeasure();
Expand Down Expand Up @@ -622,89 +709,15 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadFinished(ModuleID module_id, HR
if (module_info.assembly.name == managed_profiler_name)
{
Logger::Info("ModuleLoadFinished: ", managed_profiler_name, " - Fix PInvoke maps");

// We are in the right module, so we try to load the mdTypeDef from the target type name.
mdTypeDef nativeMethodsTypeDef = mdTypeDefNil;
auto foundType = FindTypeDefByName(nonwindows_nativemethods_type,
module_metadata->assemblyName, metadata_import, nativeMethodsTypeDef);
if (foundType)
{
// Define the actual profiler file path as a ModuleRef
WSTRING native_profiler_file = GetEnvironmentValue(environment::internal_trace_profiler_path);
if (native_profiler_file.empty())
{
native_profiler_file = GetCoreCLRProfilerPath();
}
mdModuleRef profiler_ref;
hr = metadata_emit->DefineModuleRef(native_profiler_file.c_str(), &profiler_ref);
if (SUCCEEDED(hr))
{
// Enumerate all methods inside the native methods type with the PInvokes
Enumerator<mdMethodDef> enumMethods = Enumerator<mdMethodDef>(
[metadata_import, nativeMethodsTypeDef](HCORENUM* ptr, mdMethodDef arr[], ULONG max, ULONG* cnt) -> HRESULT
{
return metadata_import->EnumMethods(ptr, nativeMethodsTypeDef, arr, max, cnt);
}, [metadata_import](HCORENUM ptr) -> void { metadata_import->CloseEnum(ptr); });

EnumeratorIterator<mdMethodDef> enumIterator = enumMethods.begin();
while (enumIterator != enumMethods.end())
{
auto methodDef = *enumIterator;

// Get the current PInvoke map to extract the flags and the entrypoint name
DWORD pdwMappingFlags;
WCHAR importName[kNameMaxSize]{};
DWORD importNameLength = 0;
mdModuleRef importModule;
hr = metadata_import->GetPinvokeMap(methodDef, &pdwMappingFlags, importName, kNameMaxSize,
&importNameLength, &importModule);
if (SUCCEEDED(hr))
{
// Delete the current PInvoke map
hr = metadata_emit->DeletePinvokeMap(methodDef);
if (SUCCEEDED(hr))
{
// Define a new PInvoke map with the new ModuleRef of the actual profiler file path
hr = metadata_emit->DefinePinvokeMap(methodDef, pdwMappingFlags,
WSTRING(importName).c_str(), profiler_ref);
if (FAILED(hr))
{
Logger::Warn("ModuleLoadFinished: DefinePinvokeMap to the actual profiler file path "
"failed, trying to restore the previous one.");
hr = metadata_emit->DefinePinvokeMap(methodDef, pdwMappingFlags,
WSTRING(importName).c_str(), importModule);
if (FAILED(hr))
{
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: Error trying to restore the previous PInvokeMap.");
}
}
}
else
{
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: DeletePinvokeMap failed");
}
}

enumIterator = ++enumIterator;
}
}
else
{
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: Native Profiler DefineModuleRef failed");
}
}
RewritingPInvokeMaps(metadata_interfaces, module_metadata, nonwindows_nativemethods_type);
RewritingPInvokeMaps(metadata_interfaces, module_metadata, appsec_nonwindows_nativemethods_type);
}
#endif

return S_OK;
}


HRESULT STDMETHODCALLTYPE CorProfiler::ModuleUnloadStarted(ModuleID module_id)
{
auto _ = trace::Stats::Instance()->ModuleUnloadStartedMeasure();
Expand Down
1 change: 1 addition & 0 deletions src/Datadog.Trace.ClrProfiler.Native/cor_profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class CorProfiler : public CorProfilerBase
//
// Helper methods
//
void RewritingPInvokeMaps(ComPtr<IUnknown> metadata_interfaces, ModuleMetadata* module_metadata, WSTRING nativemethods_type_name);
WSTRING GetCoreCLRProfilerPath();
bool GetWrapperMethodRef(ModuleMetadata* module_metadata, ModuleID module_id,
const MethodReplacement& method_replacement, mdMemberRef& wrapper_method_ref,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const WSTRING managed_profiler_full_assembly_version =
const WSTRING managed_profiler_name = WStr("Datadog.Trace");

const WSTRING nonwindows_nativemethods_type = WStr("Datadog.Trace.ClrProfiler.NativeMethods+NonWindows");
const WSTRING appsec_nonwindows_nativemethods_type = WStr("Datadog.Trace.AppSec.Waf.NativeBindings.NativeLibrary+NonWindows");

const WSTRING calltarget_modification_action = WStr("CallTargetModification");

Expand Down
29 changes: 29 additions & 0 deletions src/Datadog.Trace.ClrProfiler.Native/interop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

#include "cor_profiler.h"

#ifndef _WIN32
#include <dlfcn.h>
#include "logger.h"
#endif

EXTERN_C BOOL STDAPICALLTYPE IsProfilerAttached()
{
return trace::profiler->IsAttached();
Expand All @@ -18,3 +23,27 @@ EXTERN_C VOID STDAPICALLTYPE GetAssemblyAndSymbolsBytes(BYTE** pAssemblyArray, i
{
return trace::profiler->GetAssemblyAndSymbolsBytes(pAssemblyArray, assemblySize, pSymbolsArray, symbolsSize);
}

#ifndef _WIN32
EXTERN_C void *dddlopen (const char *__file, int __mode)
{
return dlopen(__file, __mode);
}

EXTERN_C char *dddlerror (void)
{
auto errorPtr = dlerror();

if (errorPtr)
{
trace::Logger::Error("dlerror: ", errorPtr);
}

return errorPtr;
}

EXTERN_C void *dddlsym (void *__restrict __handle, const char *__restrict __name)
{
return dlsym(__handle, __name);
}
#endif
8 changes: 6 additions & 2 deletions src/Datadog.Trace/AppSec/Security.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,12 @@ private bool AreArchitectureAndOsSupported()

private void RunShutdown()
{
_agentWriter.Shutdown();
_instrumentationGateway.InstrumentationGatewayEvent -= InstrumentationGatewayInstrumentationGatewayEvent;
_agentWriter?.Shutdown();
if (_instrumentationGateway != null)
{
_instrumentationGateway.InstrumentationGatewayEvent -= InstrumentationGatewayInstrumentationGatewayEvent;
}

Dispose();
}

Expand Down
7 changes: 4 additions & 3 deletions src/Datadog.Trace/AppSec/Waf/NativeBindings/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,9 @@ internal static void pw_freeArg(ref PWArgs input)
private static T GetDelegateForNativeFunction<T>(IntPtr handle, string functionName)
where T : Delegate
{
var initHPtr = NativeLibrary.GetExport(handle, functionName);
return (T)Marshal.GetDelegateForFunctionPointer(initHPtr, typeof(T));
var funcPtr = NativeLibrary.GetExport(handle, functionName);
Log.Debug("GetDelegateForNativeFunction - funcPtr: " + funcPtr);
return (T)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(T));
}

private static List<string> GetDatadogNativeFolders(FrameworkDescription frameworkDescription, string runtimeId)
Expand Down Expand Up @@ -319,7 +320,7 @@ private static bool TryLoadLibraryFromPaths(string libName, List<string> paths,
if (loaded)
{
success = true;
Log.Information($"Loaded library '{libName}' from '{path}'");
Log.Information($"Loaded library '{libName}' from '{path}' with handle '{handle}'");
break;
}
else
Expand Down
Loading

0 comments on commit 5080043

Please sign in to comment.