我正在开发一个 JIT 编译器,到目前为止它似乎运行良好,除了一个问题:当代码引发异常并且异常处理程序在 JITted 例程中时,操作系统会立即终止进程。当我关闭 DEP 时不会发生这种情况,因此我认为它与 DEP 相关。
当 DEP 关闭时,异常处理程序正确运行,我确保在 JITted 例程上调用 VirtualProtect,保护值为 PAGE_EXECUTE_READ,然后验证它与 VirtualQuery。
在调试器下测试它会报告 fatal error 发生在引发异常的那一刻,而不是稍后,我认为这意味着正在发生这样的事情:
有谁知道我可能做错了什么,以及如何让 DEP 接受我的异常处理程序?执行 JITted 代码本身没有任何问题。
编辑:这是生成 stub 的 Delphi 代码。它分配内存,加载基本代码,修复跳转和 try block 的修复,然后将内存标记为可执行。这是关于 DWS 的外部函数 JIT 正在进行的工作的一部分。项目。
function MakeExecutable(const value: TBytes; const calls: TFunctionCallArray; call: pointer;
const tryFrame: TTryFrame): pointer;
var
oldprotect: cardinal;
lCall, lOffset: nativeInt;
ptr: pointer;
fixup: TFunctionCall;
info: _MEMORY_BASIC_INFORMATION;
begin
result := VirtualAlloc(nil, length(value), MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
system.Move(value[0], result^, length(value));
for fixup in calls do
begin
ptr := @PByte(result)[fixup.offset];
if fixup.call = 0 then
lCall := nativeInt(call)
else lCall := fixup.call;
lOffset := (lCall - NativeInt(ptr)) - sizeof(pointer);
PNativeInt(ptr)^ := lOffset;
end;
if tryFrame[0] <> 0 then
begin
ptr := @PByte(result)[tryFrame[0]];
if PPointer(ptr)^ <> nil then
asm int 3 end;
PPointer(ptr)^ := @PByte(result)[tryFrame[2] - 1];
ptr := @PByte(result)[tryFrame[1]];
if PPointer(ptr)^ <> nil then
asm int 3 end;
PPointer(ptr)^ := @PByte(result)[tryFrame[3]];
end;
if not VirtualProtect(result, length(value), PAGE_EXECUTE_READ, oldProtect) then
RaiseLastOSError;
VirtualQuery(result, info, sizeof(info));
if info.Protect <> PAGE_EXECUTE_READ then
raise Exception.Create('VirtualProtect failed');
end;
重现问题:
dwsExternalFunctionTests 下的测试。编辑 2:这是所生成的机器代码例程的转储:
//preamble
02870000 55 push ebp
02870001 89E5 mov ebp,esp
02870003 83C4F4 add esp,-$0c
02870006 51 push ecx
02870007 53 push ebx
02870008 56 push esi
02870009 57 push edi
0287000A 8BDA mov ebx,edx
0287000C 8B33 mov esi,[ebx]
0287000E 31C0 xor eax,eax
//setup exception frame
02870010 55 push ebp
02870011 685D008702 push $0287005d
02870016 64FF30 push dword ptr fs:[eax]
02870019 648920 mov fs:[eax],esp
//procedure body
0287001C 31C9 xor ecx,ecx
0287001E 894DF8 mov [ebp-$08],ecx
02870021 8B06 mov eax,[esi]
02870023 8B5308 mov edx,[ebx+$08]
02870026 8B38 mov edi,[eax]
02870028 FF5710 call dword ptr [edi+$10]
0287002B 8945FC mov [ebp-$04],eax
0287002E 8B4604 mov eax,[esi+$04]
02870031 8B5308 mov edx,[ebx+$08]
02870034 8D4DF8 lea ecx,[ebp-$08]
02870037 8B38 mov edi,[eax]
02870039 FF571C call dword ptr [edi+$1c]
//call to a native routine. This routine raises an exception
0287003C 8B55F8 mov edx,[ebp-$08]
0287003F 8B45FC mov eax,[ebp-$04]
02870042 E8CD1FE6FD call TestStringExc
//cleanup
02870047 31C0 xor eax,eax
02870049 5A pop edx
0287004A 59 pop ecx
0287004B 59 pop ecx
//exception handler: a try/finally block to clean
//up a string variable used in the body of the code
0287004C 648910 mov fs:[eax],edx
0287004F 6864008702 push $02870064
02870054 8D45F8 lea eax,[ebp-$08]
02870057 E86870B9FD call @UStrClr
0287005C C3 ret
0287005D E98666B9FD jmp @HandleFinally
02870062 EBF0 jmp $02870054
//more cleanup
02870064 5F pop edi
02870065 5E pop esi
02870066 5B pop ebx
02870067 59 pop ecx
02870068 8BE5 mov esp,ebp
0287006A 5D pop ebp
0287006B C3 ret
这被设计为等效于(如果不完全相同)以下 Delphi 代码:
function Stub(const args: TExprBaseListExec): Variant;
var
list: PObjectTightList;
a: integer;
b: string;
//use of a string variable will introduce an implicit try-finally
//block by the compiler to handle cleanup
begin
list := args.List;
a := TExprBase(args[0]).EvalAsInteger(args.exec);
TExprBase(args[1]).EvalAsString(args.exec, b);
TestStringExc(a, b);
end;
TestStringExc 例程的目的是引发异常并确保异常处理程序正确清理字符串。
最佳答案
以下代码可能会有所帮助(来 self 自己的用于 stub 接口(interface)的编译器:
function GetExecutableMem(Size: Integer): Pointer;
procedure RaiseOutofMemory;
begin
raise EOutOfResources.Create('UnitProxyGenerator.GetExecutableMem: Out of memory error.');
end;
var
LastCommitTop: PChar;
begin
// We round the memory needed up to 16 bytes which seems to be a cache line amound on the P4.
Size := (Size + $F) and (not $F);
//
Result := MemUsed;
Inc(MemUsed, Size);
// Do we need to commit some more memory?
if MemUsed > MemCommitTop then begin
// Do we need more mem than we reserved initially?
if MemUsed > MemTop then RaiseOutOfMemory;
// Try to commit the memory requested.
LastCommitTop := MemCommitTop;
MemCommitTop := PChar((Longword(MemUsed) + (SystemInfo.dwPageSize - 1)) and (not (SystemInfo.dwPageSize - 1)));
if not Assigned(VirtualAlloc(LastCommitTop, MemCommitTop - LastCommitTop, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) then RaiseOutOfMemory;
end;
end;
initialization
GetSystemInfo(SystemInfo);
MemBase := VirtualAlloc(nil, MemSize, MEM_RESERVE, PAGE_NOACCESS);
if MemBase = nil then Halt; // VERY BAD ...
MemUsed := MemBase;
MemCommitTop := MemBase;
MemTop := MemBase + MemSize;
finalization
VirtualFree(MemBase, MemSize, MEM_DECOMMIT);
VirtualFree(MemBase, 0, MEM_RELEASE);
end.
请注意 VirtualAlloc 调用中的 PAGE_EXECUTE_READWRITE。
当进程运行时 DEP 启用以下正确运行:
type
TTestProc = procedure( out A: Integer ); stdcall;
procedure Encode( var P: PByte; Code: array of Byte ); overload;
var
i: Integer;
begin
for i := 0 to High( Code ) do begin
P^ := Code[ i ];
Inc( P );
end;
end;
procedure Encode( var P: PByte; Code: Integer ); overload;
begin
PInteger( P )^ := Code;
Inc( P, sizeof( Integer ) );
end;
procedure Encode( var P: PByte; Code: Pointer ); overload;
begin
PPointer( P )^ := Code;
Inc( P, sizeof( Pointer ) );
end;
// returns address where exceptiuon handler will be.
function EncodeTry( var P: PByte ): PByte;
begin
Encode( P, [ $33, $C0, $55,$68 ] ); // xor eax,eax; push ebp; push @handle
Result := P;
Encode( P, nil );
Encode( P, [ $64, $FF, $30, $64, $89, $20 ] ); // push dword ptr fs:[eax]; mov fs:[eax],esp
end;
procedure EncodePopTry( var P: PByte );
begin
Encode( P, [ $33, $C0, $5A, $59, $59, $64, $89, $10 ] ); // xor eax,eax; pop edx; pop ecx; pop ecx; mov fs:[eax],edx
end;
function Delta( P, Q: PByte ): Integer;
begin
Result := Integer( P ) - Integer( Q );
end;
function GetHandleFinally(): pointer;
asm
lea eax, system.@HandleFinally
end;
procedure TForm10.Button5Click( Sender: TObject );
var
P, Q, R, S, T: PByte;
A: Integer;
begin
P := VirtualAlloc( nil, $10000, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if not Assigned( P ) then Exit;
try
// ------------------------------------------------------------------------
// Equivalent
//
// A:=10;
// try
// A:=20
// PInteger(nil)^:=20
// finally
// A:=30;
// end;
// A:=40;
//
// ------------------------------------------------------------------------
// Stack frame
Q := P;
Encode( Q, [ $55, $8B, $EC ] ); // push ebp, mov ebp, esp
// A := 10;
Encode( Q, [ $8B, $45, $08, $C7, $00 ] );
Encode( Q, 10 ); // mov eax,[ebp+$08], mov [eax],<int32>
// try
R := EncodeTry( Q );
// TRY CODE !!!!
// A := 20;
Encode( Q, [ $8B, $45, $08, $C7, $00 ] );
Encode( Q, 20 ); // mov eax,[ebp+$08], mov [eax],<int32>
// REMOVE THIS AND NO EXCEPTION WILL OCCUR.
Encode( Q, [ $33, $C0, $C7, $00 ] ); // EXCEPTION: xor eax, eax, mov [eax], 20
Encode( Q, 20 );
// END OF REMOVE
// END OF TRY CODE
EncodePopTry( Q );
Encode( Q, [ $68 ] ); // push @<afterfinally>
S := Q;
Encode( Q, nil );
// FINALLY CODE!!!!
T := Q;
// A := 30;
Encode( Q, [ $8B, $45, $08, $C7, $00 ] );
Encode( Q, 30 ); // mov eax,[ebp+$08], mov [eax],<int32>
// AFter finally
Encode( Q, [ $C3 ] ); // ret
Encode( R, Q ); // Fixup try
// SEH handler
Encode( Q, [ $E9 ] ); // jmp
Encode( Q, Delta( GetHandleFinally(), Q ) - sizeof( Pointer ) ); // <diff:i32>
Encode( Q, [ $E9 ] ); // jmp
Encode( Q, Delta( T, Q ) - sizeof( Pointer ) ); // <diff:i32>
// After SEH frame
Encode( S, Q );
// A := 40;
Encode( Q, [ $8B, $45, $08, $C7, $00 ] );
Encode( Q, 40 ); // mov eax,[ebp+$08], mov [eax],<int32>
// pop stack frame
Encode( Q, [ $5D, $C2, $04, $00 ] ); // pop ebp, ret 4
// ------------------------------------------------------------------------
// And.... execute
A := 0;
try
TTestProc( P )( A );
except
;
end;
Caption := IntToStr( A )+'!1';
// Dofferent protection... execute
VirtualProtect( P, $10000, PAGE_EXECUTE_READ, nil );
A := 0;
try
TTestProc( P )( A );
except
;
end;
Caption := IntToStr( A ) + '!2';
finally
// Cleanup
VirtualFree( P, $10000, MEM_RELEASE );
end;
end;
它在 Windows 7 上工作,同时禁用和启用 DEP,并且似乎是一个最小的“JIT 代码”,其中包含一个 Delphi try-finally block 。会不会是其他/较新的 Windows 平台的问题?
关于windows - 如何防止 DEP 杀死我的 JITted 异常处理程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21756720/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为