自作VC用DLLの暗黙的リンク用ライブラリの作成ツール(ダウンロード有)
二週間作ったインポートライブラリー作成ツール。DLL持てるがLIBはない場合に使える。
このツールを使うためにDLLに暗黙的リンクについて常識は必要です。
LIB.exeツールでDEFから作ったLIBは、よく使えないことがある。特にx86
例えばcalc.dllにaddと言う関数がある。
extern "C" int __stdcall add(int, int);エクスポート名はaddだが、linkerは探すのは__imp__add@8あるいは_add@8で
LIB.exeで作ったインポートライブラリにリンクできない。
このツールを使って:
まずXMLファイルを書く、1.xmlとしてセーブする
calc.dll
add
__imp__add@8
_add@8
そしてMakeImpLib 1.xml 1.libで1.libを作成する。
LIBに何があるって知りたいなら
dumpbin /all 1.libでわかる。
なんか日本語能力の限界でよく伝えられない><
DLLインポートに詳しい人は多分私は何かを作ったのはわかるでしょう、たぶん。
DLLファイルのリソースにReadmeとヘーダーがあります。どうせソースコード付いてないから(ぁ)このDLLたち使えたいなら使ってください。ほかのは複雑だから、LibGenHelper.dllだけ見れば結構だと思う。説明は全部英語なんだけど(おい)。
DL http://www1.axfc.net/u/3157589.7z
興味ある方は、あるいはソースコード見たい方はツイッターで気軽に私と連絡して。
ちなみに、にほんご関数名にも対応する。
<?xml version="1.0" encoding="UTF-8"?>
calc.dll
add
__imp__プラス@8
_プラス@8
extern "C" int __stdcall プラス(int, int);
特定な場合で仮想メソッドの呼び出しを高速化する方法
一昨日に考えたことです。
CRC16の計算に、こういうインターフェイスを設計した。
class ICrc16CCITTの実装はこのようです。
{
public:
virtual ~ICrc16() = 0 {}
virtual void ProcessBit(bool val) = 0;
virtual void ProcessByte(char val) = 0;
virtual void ProcessBlock(char* ptr, int len) = 0;
virtual short Get() = 0;
};
class CCrc16CCITT : public ICrc16問題はProcessBitとProcessByteは仮想メソッドで、このようなメソッドを呼び出すのはとても時間を掛かる:まず仮想メソッドテーブルで関数を探しなければならない。非仮想メソッドを呼び出すときこの段階は必要がない。
{
unsigned short _rem;
public:
CCrc16CCITT()
{
_rem = 0xffff;
}
~CCrc16CCITT()
{
}
void ProcessBit(bool val)
{
bool topBit = _rem >> 15;
_rem <<= 1;
if ( (topBit ^ val) == true )
_rem ^= 0x1021;
}
void ProcessByte(char val)
{
for (int i = 7; i >= 0; --i)
ProcessBit( (val & (1 << i)) != 0 );
}
void ProcessBlock(char* ptr, int len)
{
for(int i = 0; i < len; ++i)
ProcessByte(ptr[i]);
}
short Get()
{
return _rem;
}
};
VCコンパイラーはパラメータ/Ox 付けるとき、ProcessBitを呼び出すところのソースコードは以下のアセンブルを生成する。
mov eax, DWORD PTR [ebx]300MBファイルのCRC16を計算すれば、ProcessByte関数を314572800回呼び出す。そしてもっと厳しいのはProcessBitを2516582400回呼び出す。全部
mov eax, DWORD PTR [eax+4]
test ebp, esi
setne cl
movzx edx, cl
push edx
mov ecx, ebx
call eax
ror esi, 1
dec edi
jns SHORT $LL3@ProcessByt
mov eax, DWORD PTR [ebx]こうして仮想メソッドを呼び出す。時間を無駄に使った。
mov eax, DWORD PTR [eax+4]
......
call eax
よく考えたらそうしないとダメな原因は、例えば私はCCrazyCrcのクラスを書いて
class CCrazyCrc : public CCrc16CCITT {ならばCCrc16CCITTProcessByteの関数に呼び出すProcessBitはCCrc16CCITT::ProcessBitじゃなくCCrazyCrc::ProcessBitになった。でもCRCの計算についてこの可能性がないはず。コンパイラーは「CCrc16CCITT::ProcessByteに呼び出すProcessBitは決してCCrc16CCITT::ProcessBitだ」って事実を伝えばいい。
void ProcessBit(bool val) {...}
};
そしてCCrc16CCITTはこうなった。
class CCrc16CCITT : public ICrc16VCコンパイラは/Oxパラメータ付けば生成したコードに、CCrc16CCITT::ProcessBlockメソッドにもCCrc16CCITT::ProcessByteメソッドにもアセンブル言語の「call」はなくなった。
{
unsigned short _rem;
public:
CCrc16CCITT()
{
_rem = 0xffff;
}
~CCrc16CCITT()
{
}
void ProcessBit(bool val)
{
bool topBit = _rem >> 15;
_rem <<= 1;
if ( (topBit ^ val) == true )
_rem ^= 0x1021;
}
void ProcessByte(char val)
{
for (int i = 7; i >= 0; --i)
CCrc16CCITT::ProcessBit( (val & (1 << i)) != 0 );
}
void ProcessBlock(char* ptr, int len)
{
for(int i = 0; i < len; ++i)
CCrc16CCITT::ProcessByte(ptr[i]);
}
short Get()
{
return _rem;
}
};
136MBのファイルをCRCを計算して、
前者のコードは大体15.8秒かかる。後者大体8.8秒かかる。7秒節約した。
GIMP 2.8ツールオプションに文字消えるバグの回避パッチの作成方法
GIMP 2.8 (Win32)は中国語および日本語OSで実行する時、
ツールオプションに文字はよく消える。
回避対策として環境変数のLANGをenに設定すればいい。
前に私はその「環境変数LANGをenにする」って手順をEXEに入らせる。
今はその方法より簡単な方法をここで紹介する。
使ったツールはVC++、そしてCFF Explorer。
「環境変数LANGをenにする」と言うC言語コードはこれです:
でもgimp-2.8.exe使ってるC言語ランタイムは自分のVCコンパイラーの使ってるC言語ランタイムと違ってる可能性がある。安全な方法はLoadLibraryとGetProcAddressでputenv関数のアドレスを取得する
putenv("LANG=en");
このコードをDLLファイルに入る、そしてgimp-2.8.exeの起動するときその自作DLLをロードすればパッチはできた。
HMODULE hCRT = LoadLibraryW(L"msvcrt.dll");
int (__cdecl *fPutenv)(const char*) = (int (__cdecl*)(const char*)) GetProcAddress(hCRT, "_putenv");
fPutenv("LANG=en");
#include <windows.h>
DWORD CALLBACK DllMain(HINSTANCE hDll, DWORD nReason, LPVOID pUseless)
{
if (nReason == DLL_PROCESS_ATTACH) {
HMODULE hCRT = LoadLibraryW(L"msvcrt.dll");
int (__cdecl *fPutenv)(const char*) = (int (__cdecl*)(const char*)) GetProcAddress(hCRT, "_putenv");
fPutenv("LANG=en");
}
return TRUE;
}
extern "C" __declspec(dllexport) void dummy() {}
起動するときDLLをロードしたいなら、何かの関数をインポートしないとだめです。だからそのdummyは・・・
このソースをコンパイルする:
cl /LD gimpPatch.cpp
そしてCFF Explorerの出番です。CFF Explorerでgimp-2.8.exeを開いて、
左側でImport Adder機能を選んで、右側でAdd -> gimpPatch.dll -> dummy -> Import By Name -> Rebuild Import Table。後はEXEファイルをセーブして、完成です。
gimpPatch.dllをgimp-2.8.exeと同じフォルダで放置して、gimp2.8.exe起動して試す。そのバグを回避したはず。
パッチ作成は普通でUDM Free利用すればいい。
私の使った方法はDLLの代わりに、直接そのコードをGIMP-2.8.exeに入る。
パッチ作成はNSISとVPatchを利用するのです。
GIMP 2.8.4 Win32に対応するパッチ
原理など詳しくはこちらへ→ http://d.hatena.ne.jp/sorayukinoyume/20130206
とりあえず原理など同じです。文字の消えることに対応するパッチ。
昨日記事のトラックバックからGIMPの初心者質問掲示板を発見した。
そこに2.8.4の更新情報を手に入れた。
そして今日は2.8.4のパッチ作った。
64ビットの作業環境持たなくてパッチ作れない。
これはパッチです:
http://www1.axfc.net/uploader/so/2785006.zip
対応するGIMPのインストーラーはgimp-2.8.4-setup.exeです。
よろしく
GIMP 2.8.2はWin32でツールオプションに文字が消えるバグ修正パッチ
GIMP 2.8.2は中国語版、日本語版Windowsで実行するとき、
ツールオプションに文字は時々消える。
これで使えないと思う。
BugZillaに報告すると、ほかのレポに統合された。
https://bugzilla.gnome.org/show_bug.cgi?id=668239
あのレポをよく見ると、対策はLANG=enの環境変数追加する。
GIMPのためシステムに環境変数を追加するとはちょっと気持ち悪い。
batファイルを利用するとかの対策もあるが、
互換性に考えればxcfファイルをダブルクリックなどの場合は効けない。
そしで自分はバイナリパッチ作った。
原理は簡単だ:EXEを実行する時msvcrt.dllに_putenv関数を呼ぶ。パラメタはLANG=enにする。
パッチはここでダウンロード:
http://www1.axfc.net/uploader/so/2782890.7z
NSIS + VPatchで作ったパッチ。
gimp-2.8.2-setup-1.exeでインストールやつに使える。
Audacityの改造とLRC/字幕エディターとしての利用
Audacityはオープンソースのツールから、ソースを編集して再構築すればいいですが。再構築は面倒そうなので直接バイナリを編集する方法を使った。
うほっ!WAVEエディターです。
LRCの歌詞作りたいとき、Audacityで時間を確認したら精確なタイミングを取得できる。でもやつ自身はカーソルのところのタイミングを取得するプログラミングインターフェースがなかった。
私、こいつを改造してLRCエディターの一部として使う。
最初に思いついたのは、もしAudacityはカーソルのタイミングをとあるグローバル変数に保存すれば、そのアドレスを探せば十分です。Cheat Engine使って、型は「Double」そして検索モードはUnknown Initial Valueにして、探しは難しくないはず。
いくつがある。0x2ce9a50のようなアドレスはグローバル変数に見えそうじゃない。
ええと、それじゃあどこの指令でこの変数の値を変わるって探す。0x2ce9a50を下のアドレス配列を追加する。そしてCheat engineの「Find out what writes to this address」機能を使って、Audacityでカーソルの場所を変わって簡単に探した。
指令を探したら後はaudacityを改造するのだ。
私の方法は、Audacityの起動するとき共有メモリブロックを開く。成功したら毎度fst qword ptr[ecx]した後qword ptr [ecx]の値を共有メモリにコピーする。その共有メモリブロックは自分は作った何かのLRCエディターで作る。そのコードはアセンブル言語で書く。このようです
entryのコードは、Audacityの起動するとき実行する。replaceCodeのコードは、0x4b1f8cのところで実行する。
BITS 32_shmemName:
db "81bdd671-bf82-43a9-8078-dd11039f8eeb", 0, 0, 0, 0_shmemHandle:
dd 0x12345678
_shmemAddr:
dd 0dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
dd 0x90909090
_entry:
xor eax, eax
mov dword [_shmemHandle], eax
push _shmemName
push 0
push 2
; call OpenFileMapping
dw 0x15ff
dd 0
cmp eax, 0
jle _fail
mov dword [_shmemHandle], eax
push 8
push 0
push 0
push 2
push eax
; call MapViewOfFile
dw 0x15ff
dd 0
cmp eax, 0
jz _fail_2
mov dword [_shmemAddr], eax
jmp _fail_fail_2:
mov eax, dword [_shmemHandle]
push eax
; call CloseHandle
dw 0x15ff
dd 0
_fail:
ret_replaceCode:
fst qword [ecx]
mov edx, dword [esi + 0x1c0]
push eax
mov eax, dword [_shmemAddr]
test eax, eax
jnz _cont
pop eax
ret
_cont:
push edx
mov edx, dword [ecx]
mov dword [eax], edx
mov edx, dword [ecx + 4]
mov dword [eax + 4], edx
pop edx
pop eax
ret
このコードはnasm -f binでコンパイルしてバイナリコードになる。
CFF Explorerというツールを利用してAudacityのSection HeaderにAdd Section (File Data)。このバイナリコードをaudacityにインポートする。Change section flagsでIsReadableとIsWritableとIsExecutable全部On。そしてCFF ExplorerのImport Adder機能でCreateFileMappingAとMapViewOfFileとCloseHandleのインポートテーブルを作る。セーブ。
まだまだ足りない。ollydbgの出番だ。さっき言ったことを編集した後AudacityをOllydbgで開いて、
こういうコードを見える。JMP 00707DF3を編集して、さっきインポートするファイルにたくさんの90(NOP)があるだろう?そこにジャンプする。
007080B3 > $ E8 E0030000 CALL audacity.00708498
007080B8 .^ E9 36FDFFFF JMP audacity.00707DF3
あと、004B1F8Cのところのコードは編集して(2つのコードをオーバーライトする)replaceCodeの部分にcallする。これで現有コードの編集は終わりました。セーブして(copy to executable -> all modifications)。
再度OllyDbgでAudacity.exeを開く。今度はさっきインポートしたファイルの部分を編集する。まずあの90等。一部をPUSH 00707DF3に編集する。そうするとretの時元のところに戻れる。
後、インポートしたファイルのコードに全部のshmemName、shmemHandle、shmemAddrのような所のオペランドを修復しなければならない。nasmでコンパイルする時基準アドレスは0にしてたからね。
最後、インポートしたファイルのコードに全部のff15(call)指令のオペランドをつめる。さっき追加したインポートテーブルに対応のアドレスで。
そしてセーブする(編集したところ全部選択してそして copy to executable -> selection)
こうして、カーソルのタイミングは共有メモリから取得できる。例えC#で
using System;
using System.Runtime.InteropServices;namespace Sorayuki
{
public class AudacityPositionReader : IDisposable
{
[DllImport("Kernel32.dll", EntryPoint="CreateFileMappingA", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.StdCall)]
static private extern UInt32 CreateFileMappingA(UInt32 hFile, IntPtr lpAttributes, UInt32 flProtect, UInt32 dwMaximumSizeHigh, UInt32 dwMaximumSizeLow, string lpName);[DllImport("Kernel32.dll", EntryPoint="OpenFileMappingA", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.StdCall)]
static private extern UInt32 OpenFileMappingA(UInt32 dwAccess, UInt32 bInherit, string lpName);[DllImport("Kernel32.dll", EntryPoint="MapViewOfFile", CallingConvention=CallingConvention.StdCall)]
static private extern IntPtr MapViewOfFile(UInt32 hFileMapping, UInt32 dwAccess, UInt32 dwOffsetHigh, UInt32 dwOffsetLow, UInt32 dwLen);[DllImport("Kernel32.dll", EntryPoint = "UnmapViewOfFile", CallingConvention = CallingConvention.StdCall)]
static private extern UInt32 UnmapViewOfFile(IntPtr pBase);[DllImport("Kernel32.dll", EntryPoint = "CloseHandle", CallingConvention = CallingConvention.StdCall)]
static private extern UInt32 CloseHandle(UInt32 handle);private UInt32 _hShmem;
private IntPtr _pShmem;const string _shmemName = "81bdd671-bf82-43a9-8078-dd11039f8eeb";
public AudacityPositionReader()
{
UInt32 hShmem = OpenFileMappingA(3, 0, _shmemName);
if (hShmem == 0)
{
hShmem = CreateFileMappingA(0, IntPtr.Zero, 4, 0, 8, _shmemName);
if (hShmem == 0)
throw new Exception("Can't create shared memory block");
}_hShmem = hShmem;
IntPtr pShmem = MapViewOfFile(hShmem, 2, 0, 0, 8);
if (pShmem == IntPtr.Zero)
{
CloseHandle(hShmem);
_hShmem = 0;
throw new Exception("Can't map shared memory block");
}
_pShmem = pShmem;
}~AudacityPositionReader()
{
Dispose();
}public void Dispose()
{
UnmapViewOfFile(_pShmem);
CloseHandle(_hShmem);
}public double Position
{
get
{
unsafe
{
double* pVal = (double*)_pShmem;
return *pVal;
}
}
}
}
}
改造したAudacityおよびソースコード:
http://www1.axfc.net/uploader/so/2751946
仮想マシンでXen+NetBSD Dom0遊ぶ
しばらくDomUの準仮想化OSをインストールしていない。
こうする理由は特になかった。タイトルの言う、遊び。
まずはVMWare Playerの仮想マシンにNetbsd 6.0/i386をインストールする。
この段階は簡単です。普通のOSインストールと特に何か違いはない。
インストール完了後、cdromの/i386/binary/kernel/netbsd-XEN3PAE_DOM0.gzをハードディスクにコピーする。これはXenに必要なDOM0カーネル。Xen 4.1.3で非PAEのカーネルはDOM0として使えないようだ。
そしてpkgsrcを取得する。これはnetbsdのインストールするとき取得できる。あるいは、cdromの /sysinst ツールを起動してConfigメニューにpkgsrcをダウンロード&インストールする。
Xenを入れるためインストールするものは二つ。xentools41とxenkernel41です。二つも/usr/pkgsrc/sysutilsに入る。気軽にmakeしてできる・・・はず。だができない。少なくとも私は失敗した。
xentoolsを構築するためbashは必要です。bashを構築するためgtexinfoは必要です。gtexinfoを構築するためbashは必要です。死ぬ。
仕方ない、bashのバイナリパッケージをインストールする。そしてxentools41を再構築する、今度はできた。
xentools41とxenkernel41を構築完了した後make installを実行する。こうしてOSに入れた。
この段階は終わった後、/usr/pkg/xen41-kernelにXenのカーネルは置いてる。前にハードディスクにコピーしたnetbsd-XEN3PAE_DOM0.gzまだ覚えてる?解凍して / で置いていい。そして /boot.cfg を編集して、Xenで起動のアイテムを追加する
menu=Boot in Xen:load /netbsd-XEN3PAE_DOM0 console=pc; multiboot /usr/pkg/xen41-kernel/xen.gz dom0_mem=384M
dom0_memはNetbsd DOM0に保留するメモリ。赤字の部分は注意して。なければNetBSDの起動から後何も見えなくなった
ここまでは足りない。xendなどのdaemonは入っていないからxmなどのツールは使えない。でもxentoolsをインストールした後 /etc/rc.d にxendなど見つからない。実はやつらは /usr/pkg/share/examples/rc.d にある。xend xencommons xendomainsを/etc/rc.dにコピーする。そして /etc/rc.confに
xend=YES
xencommons=YES
xendomains=YES
を追加する。
システム再起動。boot menuにBoot in Xenを選べば起動する後 xm list でdom0が出る。
下記は参考した文章です:
http://pbraun.nethence.com/doc/sysutils_xen/dom0_netbsd.html
http://www.daemon-systems.org/man/boot.cfg.5.html