"DLL 지옥": 광범위한 상황에서 힌트와 기술을 피하십시오.

어떤 FFI(외국어 기능 인터페이스)를 통해 VAST Platform (VA Smalltalk) 등 고급 언어에서 C, C++, Rust 등 언어로 임무를 위임하는 것은 갈수록 흔해진다.
이상적인 상황에서 당신은 모든 내용을 당신이 좋아하는 고급 언어로 실현하기를 원하지만, 나는 모든 문제에 대해 적당한 도구를 사용해야 한다고 믿습니다.때로는 더 높은 성능을 필요로 하고, 때로는 이미 다른 언어로 이루어진 라이브러리를 실현하고 유지하는 비용을 절약하고 싶어 합니다.여러 가지 이유가 있을 수 있지만, 이 경우, 이 라이브러리에 대한 얇은 FFI 귀속 (포장기라고도 함) 을 만듭니다.
그런 다음 고급 언어를 사용하고 DLL로 컴파일된 C 라이브러리의 함수를 호출하려면 DLL(동적 링크 라이브러리)을 검색하여 응용 프로그램 프로세스 공간에 불러와야 합니다.
이것은 간단한 절차로 들릴 수 있지만, 개발자가 많은 시간을 들여 문제의 원인을 찾아내려고 하는 상황 중 하나일 수 있다. 왜 DLL을 불러올 수 없는지, 왜 다른 DLL을 불러왔는지, 왜 예상한 DLLL이 아닌 다른 경로를 가지고 있는지 등이다.이것은 흔히 볼 수 있는 문제로 광범위하게'DLL Hell'이라고 불린다.

본 논문에서는 DLL을 광역 플랫폼에서 불러올 때 이러한 상황을 벗어날 수 있도록 힌트, 기교, 스크립트를 보여 드리겠습니다.

문제 해결 VAST.규범


VAST에서 대부분의 경우 서로 다른 패키지의 라이브러리 파일 이름은 .ini 절의 주 [PlatformLibrary Name Mappings] 파일에 지정됩니다.
예를 들어, 다음과 같이 발췌합니다.

[PlatformLibrary Name Mappings]
; Almost all .DLL files referenced by VA Smalltalk have aliases (logical names) that are mapped here.
;
; The keywords (logical names) are case sensitive; the values (dll names) are not.
Abt_Primitives=
AbtNativeQ=abtvof40
AbtNativeV=abtvmf40
BROTLI_LIB=esbrotli40
CgImageSupport=escgi40
CRYPTO_LIB=libcrypto

이렇게 하면 주어진 논리 이름(예: libcrypto)의 물리적 이름(예: CRYPTO_LIB)을 사용자 정의할 수 있습니다.다른 이름, 심지어 전체 경로를 사용할 수 있습니다. 예를 들면:

CRYPTO_LIB=my-special-libcrypto
CRYPTO_LIB=c:\Users\mpeck\Documents\Instantiations\libs\OpenSSL_x64\libcrypto

그러면... 전형적인 문제와 가능한 해결 방안을 봅시다.

이 점을 확인해 주십시오.ini 파일 읽는 중


DLL이 올바르게 불러오지 않은 이유를 깊이 이해하고 .ini개의 파일을 무작위로 편집하기 전에 정확한 파일을 편집하고 있는지 확인하십시오.
이것은 분명할 수 있지만, VAST는 .bat 파일 체인에서 시작되었거나 (최종 VAST를 실행하는 정확한 줄을 찾기 어렵다), 또는 사용자 정의 .ini files(최종 사용자 응용 프로그램에서 읽는다)도 많이 보였다.이러한 상황에서 어떤 파일이 VAST가 시작할 때 직접 읽은 .ini 파일인지 알고, 논리적 이름을 물리적 이름으로 비추는 것은 항상 쉽지 않다.
첫 번째 솔루션은 시스템이 시작할 때 읽은 .ini 파일을 묻는 것입니다.

Transcript 
    cr; 
    show: 'INI File: ';
    show: System primitiveIniFileFullPathAndName asString.

이것은 개발 이미지나 실행 중인 이미지에 문제가 있을 때만 도움이 됩니다. 이 정보를 수정하고 다시 포장해서 인쇄할 수 있습니다.
그러나 런타임 이미지에서 문제가 발생하면 재포장할 수 없으므로 기본 .ini 파일을 찾는 VM의 정확한 알고리즘을 이해하는 데 도움이 됩니다.

 /**
 * Finds the .INI filename.
 * This is called AFTER EsFindImageFileName().
 * Algorithm:
 * 1. If globalInfo->iniName has a name in it (came from -ini: commandline switch),
 * look for (globalInfo->iniName).ini file, answer TRUE if present; else answer FALSE.
 * 2. Look for (globalInfo->imagePath)(globalInfo->imageName).ini, answer TRUE if present.
 * 3. Look for <exePath><exeName>.ini, answer TRUE if present.
 * 4. Look for es.ini, answer TRUE if present.
 * 5. Answer FALSE.
 */
BOOLEAN EsFindIniFileName(EsGlobalInfo *globalInfo)

이렇게 하면 사용 중인 파일을 찾을 때까지 이 파일들의 존재를 단계적으로 수동으로 검사할 수 있습니다.

BTW: VAST 2021 (v10.x.x) is including this information as part of the default walkback header.


확실합니다.ini 편집 고려 중


마찬가지로, 이것은 매우 뚜렷하게 들리지만, 나는 일부 최종 사용자 응용 프로그램이 기본적인 광역 시작 메커니즘을 덮어쓰는 것을 본 적이 있다. 자신의 .ini 읽기, 하드 인코딩 설정이 있는데, 단지 몇 가지 예를 들면...
따라서 문제를 해결하기 위해 올바른 .ini 파일을 수정하기 시작하면 변경 사항이 고려되었는지 확인하십시오.다음 코드는 CRYPTO_LIB이 시작된 후에 비치는 구체적인 내용을 조회했습니다.

| logicalName aliases sharedLibs lib |
logicalName := 'CRYPTO_LIB'.
aliases := PlatformLibrary classPool at: 'Aliases'.
sharedLibs := PlatformLibrary classPool at: 'SharedLibraries'.
Transcript show: 'Alias Key: ', logicalName, ' Value: ', (aliases at: logicalName); cr.
lib := sharedLibs detect: [:each | each logicalName= logicalName] ifNone: [nil].
Transcript show: 'SharedLibraries LogicalName: ', lib logicalName, ' PhysicalName: ', lib physicalName. 

예를 들어, 이 줄로 .ini 파일을 편집하는 경우:

CRYPTO_LIB=c:\Users\mpeck\Documents\Instantiations\libs\OpenSSL_x64\libcrypto

그러나 위의 코드는 libcrypto을 인쇄했는데 이것은 어떤 이유로 나의 변경이 무시되었다는 것을 의미한다.그래서위의 코드가 파일에 지정한 내용을 인쇄했는지 확인하십시오.

DLL 로드 실패 문제 해결


기본 .ini VAST를 읽고 변경 사항이 고려되는 경우 DLL 로드에 실패한 원인이나 오류를 제거하기 시작할 수 있습니다.

Windows DLL 찾기 순서


DLL을 로드할 때 가장 흔히 볼 수 있는 문제는 Windows가 검색 메커니즘을 통해 찾을 수 없거나 찾을 수 없지만 버전이 예상과 다르다는 것입니다.
만약에 정부의 documentation about the lookup mechanism을 보신다면 등록표 항목, 예를 들어 KnownDLLs or SafeDllSearchMode, LOAD_WITH_ALTERED_SEARCH_PATH을 통해 사용자 정의 불러오기 등등...대부분의 경우 기본값 비헤이비어가 사용됩니다.
일반적으로 작업 디렉터리를 식별하기 어렵고, 방대한 실행 가능한 바이너리 파일이 무엇인지/어디에 있는지, 어떤 디렉터리가 GetSystemDirectory 또는 GetWindowsDirectory 등등.
다음 스크립트는 Windows 검색 순서를 시뮬레이션하여 각 단계의 디렉토리를 표시합니다.

| string m p | 
string := String new: 256.

m := OSHmodule getModuleHandle: nil.
m isNull ifFalse: [
  p := String new: 256.
  m getModuleFileName: p cbFileName: p size.
  Transcript show: '1: The directory containing the EXE file: ', p trimNull ; cr.
] ifTrue: [Transcript show: 'Can''t find module!'; cr.].

OSCall new getSystemDirectory: string cbSysPath: 256.
Transcript show: '2: GetSystemDirectory : ', string; cr.

OSCall new getWindowsDirectory: string cbSysPath: 256.
Transcript show: '4: GetWindowsDirectory : ', string; cr.

Transcript show: '5: Current Directory : ', CfsDirectoryDescriptor getcwd; cr.

Transcript show: '6: $Path : ', 'Path' abtScanEnv; cr.

스크립트는 다음과 같이 인쇄할 수 있습니다.

1: The directory containing the EXE file: Z:\Common\Development\VAST\10.0.0x64-b466\10.0.0x64\abt.exe
2: GetSystemDirectory : C:\WINDOWS\system32
4: GetWindowsDirectory : C:\WINDOWS
5: Current Directory : Z:\Common\Development\Images\10.0.0.x64-b466-dev
6: $Path : Z:\Common\Development\VAST\10.0.0x64-b466\10.0.0x64\;Z:\Common\Development\VAST\10.0.0x64-b466\10.0.0x64;C:\Python27\;C:\Python27\Scripts;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\PuTTY\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\Git\cmd;C:\Program Files\LLVM\bin;C:\Program Files\CMake\bin;C:\Users\mpeck\AppData\Local\Programs\Python\Python37\Scripts\;C:\Users\mpeck\AppData\Local\Programs\Python\Python37\;C:\Users\mpeck\AppData\Local\Microsoft\WindowsApps;C:\Program Files\PuTTY;C:\Users\mpeck\AppData\Local\Microsoft\WindowsApps;

이제 Windows에서 찾을 디렉토리와 순서를 알 수 있습니다.

Pro Tip: Are you running VAST 32-bit on Windows 64-bit? If so, bear in mind that the function GetSystemDirectory will answer C:\WINDOWS\system32 even though the real physical directory mapped to it is C:\Windows\SysWOW64\. You can read this post for more details.


아, 참고로 불러오는 순서에 영향을 미칠 수 있는 Windows 등록표 항목을 더 얻고 싶으세요?너도 광대한 것에서 해낼 수 있어!
다음 예제에서는 SafeDllSearchMode의 값을 가져옵니다.

"Registry Hive: HKEY_LOCAL_MACHINE
Registry Path: \System\CurrentControlSet\Control\Session Manager\

Value Name: SafeDllSearchMode

Value Type: REG_DWORD
Value: 1
"

| subKey buffer key answer valueName |

subKey := ByteArray new: 4.
buffer := String new: 256.
key := 'System\CurrentControlSet\Control\Session Manager'.
valueName := 'SafeDllSearchMode'.

 answer := (OSHkey immediate: PlatformConstants::HkeyLocalMachine)
    regOpenKeyEx: key asParameter
    ulOptions: 0
    samDesired: PlatformConstants::KeyQueryValue
    phkResult: subKey.

 answer = PlatformConstants::ErrorSuccess
  ifTrue: [
    subKey := (OSHkey immediate: subKey abtAsInteger )
        regQueryValueEx: valueName
        lpReserved: 0
        lpType: nil
        lpData: buffer asParameter
        lpcbData: (OSUInt32 new uint32At: 0 put: buffer size; yourself)
asParameter.

  (buffer asByteArray uint64At: 0) inspect.
  ]. 

로드된 DLL의 경로는 무엇입니까?


DLL을 처리할 때의 또 다른 일반적인 문제는 일부 광역 이미지나 기계의 경우 작동할 수 있지만 다른 기계의 경우 작동할 수 없다는 것입니다. 만약 .ini 파일에 이 DLL의 전체 경로를 지정하지 않았다면 Windows가 이 두 가지 상황에서 추출한 정확한 DLL(경로 포함)을 이해하는 데 매우 유용할 것입니다.
이를 위해 다음 스크립트를 사용할 수 있습니다.

"Disclaimer: this assumes you already loaded the dll into the running process"
 | m p |
 m := OSHmodule getModuleHandle: 'libeay32.dll' asPSZ.
m isNull ifFalse: [
  p := String new: 256.
  m getModuleFileName: p cbFileName: p size.
  Transcript show: 'Loaded DLL: ', p trimNull.
] ifTrue: [Transcript show: 'Can''t find module!'; cr.] 

다음과 같이 인쇄합니다.

Loaded DLL: z:\Common\Development\Images\10.0.0.x64-b466-dev\libeay32.dll

다른 방법(VAST 외)은 에 설명된 대로 Process Explorer 도구를 사용하는 것입니다.

DLL 종속성 확인


"DLL이 있을 것을 맹세하지만 VAST는 로드할 수 없습니다."라고 생각하는 경우가 많습니다.로드하려는 DLL이 올바른 위치에 있을 수 있지만 Windows에서 로드하려고 하는 것은 맞지만 Windows에서 로드할 수 없습니다. 이 DLL에 대한 종속성을 찾을 수 없기 때문입니다.
예를 들어... ssleay32.dll을 불러오려고 했지만 실패했다고 가정하면dumpbin 도구를 사용하여 DLL의 종속성을 식별할 수 있으며 다음 사항을 빠뜨리지 않습니다.

보시다시피 이 예에서 ssleay32.dlllibeay32.dll(및 기타)에 의존합니다.따라서... libeay32.dll 또는 기타 의존 항목을 찾을 수 없는 경우 루트 DLL(이 예에서는 ssleay32)의 로드가 실패합니다.
항상 연관성을 확인합니다.

실행 파일 및 DLL 비트 확인


이전 질문과 달리 "DLL이 있는 것으로 맹세하지만 VAST는 로드할 수 없습니다."32비트와 64비트 프로그램을 동시에 사용하면 결국 이 문제가 발생할 수 있습니다.DLL을 보고 Windows가 불러올 수 있어야 한다고 생각하지만 불러올 수 없습니다. 의존 관계가 좋더라도 다른 검사할 것은 비트입니다.VAST 64비트는 64비트 DLL만 로드할 수 있습니다. 32비트도 마찬가지입니다.
이를 위해 나는 대량의 스크립트를 사용하지 않고 simple technique using a text editor을 사용했다.이 점을 기억하는 것은 매우 유용하다.

결론


이 힌트와 기교가 당신에게 유용하길 바랍니다!너는 또 다른 비결을 공유해야 하니?듣고 싶어요.

좋은 웹페이지 즐겨찾기