I'm trying to stalk a minimal Java program that uses JNI with Frida, but I always run into access violation crashes when the JVM is shut down.
Furthermore, I only seem to ever be able to successfully stalk a JVM process (even if JIT is disabled with -Xint), if Stalker.trustThreshold is set to -1.
If I set the Stalker.trustThreshold to anything other than -1, the JVM does not crash, but the stalker does not cover any BBs from my custom JNI library native.dll (see below).
Here's my Java program:
// Main.java
public class Main {
static {
System.loadLibrary("native");
}
public static void main(String[] args) throws Exception {
Thread.sleep(20_000); // Enough time to attach to process...
new Main().sayHello();
}
private native void sayHello();
}
// Main.cpp
#include "Main.h"
#include <iostream>
#include <fstream>
#include <windows.h>
void sayHello() {
std::ofstream file;
file.open("output.tmp");
file << "Hello World\n";
file.close();
std::cout << "Current Thread id = " << GetCurrentThreadId() << "\n";
}
JNIEXPORT void JNICALL Java_Main_sayHello
(JNIEnv *, jobject) {
sayHello();
}
# Compile native library (Windows)
cl.exe /LD /EHsc /I "%JAVA_HOME%\include" /I "%JAVA_HOME%\include\win32" Main.cpp /link /DLL /DEBUG /OUT:native.dll
# Compile Java code
javac Main.java
# Run program
java -cp . -Djava.library.path=%cd% Main
My frida script looks as follows:
# frida-agent.py
import sys
import frida
def on_message(message, data):
print(str(message))
def main():
device = frida.get_local_device()
pid = -1
for proc in device.enumerate_processes():
if sys.argv[1] in [str(proc.pid), proc.name]:
pid = proc.pid
break
session = device.attach(pid)
print("Attaching to PID=" + str(pid))
script = session.create_script(
"""
Stalker.trustThreshold = -1;
const moduleMap = new ModuleMap();
moduleMap.update();
const createCoverageMap = (events) => {
moduleMap.update();
const coverageMap = {};
for (const event of events) {
const [start, _] = event;
const pStart = new NativePointer(start);
const module = moduleMap.find(pStart);
if (module) {
const offset = pStart.sub(module.base).toInt32();
if (!(module.path in coverageMap)) {
coverageMap[module.path] = 0;
}
coverageMap[module.path] += 1;
}
}
return coverageMap;
};
const stalkThread = (threadId) => {
Stalker.follow(threadId, {
events: {
call: false,
ret: false,
exec: false,
block: false,
compile: true,
},
onReceive: function (events) {
const bbEvents = Stalker.parse(events, {stringify: false, annotate: false});
send({coverage: createCoverageMap(bbEvents)});
}
});
}
const currentThreads = Process.enumerateThreads();
for (const thread of currentThreads) {
console.log("Stalking thread: " + thread.id);
stalkThread(thread.id);
}
"""
)
script.load()
script.on("message", on_message)
sys.stdin.read()
sys.exit(0)
if __name__ == "__main__":
main()
When attaching with python frida-agent.py 8232 the generated output is:
Current Thread id = 9632
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000287f70dae25, pid=8232, tid=0x00000000000025a0
#
# JRE version: OpenJDK Runtime Environment (8.0_292-b10) (build 1.8.0_292-b10)
# Java VM: OpenJDK 64-Bit Server VM (25.292-b10 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C 0x00000287f70dae25
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\workspace\simple-jni-app\hs_err_pid8232.log
#
# If you would like to submit a bug report, please visit:
# https://github.com/AdoptOpenJDK/openjdk-support/issues
#
Do you have any idea on (1) how to prevent the JVM to crash and (2) set the Stalker.trustThreshold to anything other than -1 and still cover BBs from native.dll?
Help is very much appreciated! Thanks!
Related
I am doing WinApi C++ programming with my colleague. We are using the visual studio. While I was loading the image, there was an error like the one in the picture below.
While debugging, I found out that this is an error caused by the lack of files on the path. You can simply add a file to the path to resolve the error, but you want to import the image from another specified path. The reason why you don't choose a simple method is that even though your colleagues use the same code, they succeed in importing files from different paths, so they don't get errors.
The path where I import the file is 'D:\BreakTogether\OutPut\bin\Res', and the path where my colleague imports the file is 'D:\BreakTogether\bin\Res'. Below is the code used to get the file path.
#include "pch.h"
#include "ResMgr.h"
#include "PathMgr.h"
#include "Image.h"
ResMgr::ResMgr()
{
}
ResMgr::~ResMgr()
{
/*map<wstring, Image*>::iterator iter;
for (iter = m_mapImg.begin(); iter != m_mapImg.end(); ++iter)
{
delete iter->second;
}*/
Safe_Delete_Map(m_mapImg);
}
Image* ResMgr::ImgLoad(const wstring& _strKey, const wstring& _strRelativePath)
{
Image* pImg = ImgFind(_strKey);
if (nullptr != pImg)
{
return pImg;
}
wstring strFilePath = PathMgr::GetInst()->GetRsrcPath();
strFilePath += _strRelativePath;
pImg = new Image;
pImg->Load(strFilePath);
pImg->SetKey(_strKey);
pImg->SetRelativePath(_strRelativePath);
m_mapImg.insert(make_pair(_strKey, pImg));
// m_mapImg.insert({ _strKey , pImg });
return pImg;
}
Image* ResMgr::ImgFind(const wstring& _strKey)
{
auto iter = m_mapImg.find(_strKey);
if (iter == m_mapImg.end())
{
return nullptr;
}
return static_cast<Image*>(iter->second);
}
#include "pch.h"
#include "PathMgr.h"
#include "Core.h"
PathMgr::PathMgr()
: m_szRsrcPath{}
{
}
PathMgr::~PathMgr()
{
}
void PathMgr::Init()
{
GetCurrentDirectory(255, m_szRsrcPath);
int Length = wcslen(m_szRsrcPath);
for (int i = Length - 1; i >= 0; i--)
{
if (m_szRsrcPath[i] == '\\')
{
m_szRsrcPath[i] = '\0';
break;
}
}
wcscat_s(m_szRsrcPath, 255, L"\\bin\\Res\\");
SetWindowText(Core::GetInst()->GetWndHandle(), m_szRsrcPath);
}
I tried adding an image to the path where I load the file. That will solve the problem. However, it is inefficient because my colleague and I have to add the same file twice because we have different paths to load the image.
If you can load the file normally when the image file is in place, there is no problem with your code.
Go to your Project Property Pages -> Debugging -> Working Directory and align your path with your colleagues.
If the problem is not solved, plz tell me more about your problem.
Have a nice day!
First some context, i got two nodejs native addon. The first one contains a static c++ object "Conn" exposed using an v8 object internal field as described in the embedder's guide
NAN_METHOD(cobject) {
auto isolate = Isolate::GetCurrent();
Conn* p = &ConnHolder::connection;
Local<ObjectTemplate> conn_templ = ObjectTemplate::New(isolate);
conn_templ->SetInternalFieldCount(1);
Local<Object> obj = conn_templ->NewInstance();
obj->SetInternalField(0, External::New(isolate, p));
info.GetReturnValue().Set(obj);
}
In my other native addon, i'm loading the first one using c++ code and i expose a function called test containing two calls on the Conn object "callToDummyFunction()" and "callToFunctionWithMemberAccess()"
// persistent handle for the main addon
static Persistent<Object> node_main;
void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
// get `require` function
Local<Function> require = module->Get(String::NewFromUtf8(isolate, "require")).As<Function>();
Local<Value> args[] = { String::NewFromUtf8(isolate, "path_to_my_addon\\addon.node") };
Local<Object> main = require->Call(module, 1, args).As<Object>();
node_main.Reset(isolate, main);
NAN_EXPORT(exports, test);
}
NAN_METHOD(test) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
// get local handle from persistent
Local<Object> main = Local<Object>::New(isolate, node_main);
// get `cobject` function to get pointer from internal field
Local<Function> createdMain = main->Get(String::NewFromUtf8(isolate, "cobject")).As<Function>();
Local<Object> callResult = createdMain->Call(main, 0, nullptr).As<Object>();
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
// from there i get a pointer to my Conn object
Conn* con = static_cast<Conn*>(ptr);
conn->callToDummyFunction();
conn->callToFunctionWithMemberAccess();
info.GetReturnValue().Set(10);
}
Then i'm launching a nodejs session using "node", i load the first and second addon using two require calls and finally i'm calling the method test on the second addon.
The method test is executed, the call to "callToDummyFunction" is executed successfully but the call to "callToFunctionWithMemberAccess" crash and also kill the node session.
Ok, so what is the difference between "callToDummyFunction" and "callToFunctionWithMemberAccess" ?
bool Conn::callToDummyFunction()
{
cout << "callToDummyFunction" << endl;
return true;
}
bool Conn::callToFunctionWithMemberAccess()
{
cout << "callToFunctionWithMemberAccess " << someIntMember << endl;
return true;
}
So, it seems accessing a member of the Conn object generate an error and crash the node session. The node session does not output any message before crashing.
Can someone tell me why?
And/Or
How to get an error message ?
I'm answering my own question. In fact, i'm stupid but at least my stupidity made me learn some strange cpp things.
So, first of all the stupid answer. Instead of using my returned object i'm using a totaly unrelated object :(
Local<Object> callResult = createdMain->Call(main, 0, nullptr).As<Object>();
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
Why using
Local self = info.Holder();
instead of callResult. The right code would be
Local<Object> callResult = createdMain->Call(main, 0, nullptr).As<Object>();
Local<External> wrap = Local<External>::Cast(callResult->GetInternalField(0));
What did i learn from this stupid mistake:
read your code carefully (obvious)
executing member function on nullptr actualy works if there isn't any member access in the function (maybe it's obvious for an experienced cpp dev)
Native Addons live in their own vm, static fields aren't shared between vms.
I am developing a desktop Air application that uses an Air Native Extension (ANE). The native part of the ANE is composed only by a DLL written partially in C and partially in C++. The app was compiled with Visual Studio 2010 and requires the MSVCR100.DLL and the MSVCP100.DLL to be on the same directory as the application's exe file.
The app and DLL work great on many computers but on clean Windows 7 SP1 computers, part of its code makes the DLL crash.
I've narrowed down the conflicting code to the following:
// Addresses.
String^ defaultGateway = "Not Found";
String^ interfaceIPAddress = "Not Found";
String^ interfaceMask = "Not Found";
array<NetworkInterface^>^nics = NetworkInterface::GetAllNetworkInterfaces();
if (nics != nullptr || nics->Length > 0)
{
System::Collections::IEnumerator^ myEnum4 = nics->GetEnumerator();
while (myEnum4->MoveNext())
{
NetworkInterface^ adapter = safe_cast<NetworkInterface ^>(myEnum4->Current);
IPInterfaceProperties^ properties = adapter->GetIPProperties();
GatewayIPAddressInformationCollection^ gateways = properties->GatewayAddresses;
for each (GatewayIPAddressInformation^ gateway in gateways)
{
if (gateway->Address->AddressFamily == AddressFamily::InterNetwork)
{
defaultGateway = gateway->Address->ToString();
for each (UnicastIPAddressInformation^ unicastIPAddressInformation in properties->UnicastAddresses)
{
if (unicastIPAddressInformation->Address->AddressFamily == AddressFamily::InterNetwork)
{
interfaceIPAddress = unicastIPAddressInformation->Address->ToString();
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
}
}
}
}
}
}
Just to give you more context, I'll copy the entire function were that code is:
FREObject MainInterfaceInfo(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
{
// Addresses.
String^ defaultGateway = "Not Found";
String^ interfaceIPAddress = "Not Found";
String^ interfaceMask = "Not Found";
array<NetworkInterface^>^nics = NetworkInterface::GetAllNetworkInterfaces();
if (nics != nullptr || nics->Length > 0)
{
System::Collections::IEnumerator^ myEnum4 = nics->GetEnumerator();
while (myEnum4->MoveNext())
{
NetworkInterface^ adapter = safe_cast<NetworkInterface ^>(myEnum4->Current);
IPInterfaceProperties^ properties = adapter->GetIPProperties();
GatewayIPAddressInformationCollection^ gateways = properties->GatewayAddresses;
for each (GatewayIPAddressInformation^ gateway in gateways)
{
if (gateway->Address->AddressFamily == AddressFamily::InterNetwork)
{
defaultGateway = gateway->Address->ToString();
for each (UnicastIPAddressInformation^ unicastIPAddressInformation in properties->UnicastAddresses)
{
if (unicastIPAddressInformation->Address->AddressFamily == AddressFamily::InterNetwork)
{
interfaceIPAddress = unicastIPAddressInformation->Address->ToString();
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
}
}
}
}
}
}
String^ result = interfaceIPAddress + ";" + interfaceMask + ";" + defaultGateway;
// Converting the response to const uint8_t *
msclr::interop::marshal_context oMarshalContext;
const char* charResponse = oMarshalContext.marshal_as<const char*>(result);
const uint8_t * resultNativeCharArray = (const uint8_t *)charResponse;
uint32_t resultNativeCharArrayLength = 0;
while (true) {
if (NULL == resultNativeCharArray[resultNativeCharArrayLength]) {
break;
}
else {
resultNativeCharArrayLength++;
}
}
FREObject functionResult;
FRENewObjectFromUTF8(resultNativeCharArrayLength, resultNativeCharArray, &functionResult);
return functionResult;
}
I'm a noob on C and C++ because I only saw it a couple of times 10 years ago so I have no clue about what exactly is making the DLL crash. Can someone tell? Any advice will be more than appreciated.
Editted
I came to realize that the same narrowed code makes the app require the MSVCR100.DLL and the MSVCP100.DLL. If I remove that portion of code, the app can run without them.
MSVCP100.DLL contains the standard C++ Library; MSVCR100.DLL is the C runtime. It's probably something like the safe_cast call that's introducing the dependency. More details about the runtime libraries are here.
I'd suggest using the official redistributable package to deploy the Visual C++ runtimes DLLs instead of deploying them into your app's directory yourself. See Redistributing Visual C++ Files for details... the walkthrough links in there will be helpful to you. That's the sure-fire way to be sure that the target system has all the dependencies you'll need. Your older (non-clean) Win7 systems probably had some other app or Windows Update install that redistributable for you, which is why your code is working there.
Actually, the line of code that was messing everything up was:
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
If that line was deleted, the DLL would run without a problem.
The issue occurred because one of the inactive network interfaces of the system reported '0.0.0.0' on gateway->Address->ToString(). So when trying to unicastIPAddressInformation->IPv4Mask->ToString() the DLL would crash.
To solve the issue I just added an if (adapter->OperationalStatus == OperationalStatus::Up) at the beggining and everything worked great afterwards. The resulting code is as follows:
while (myEnum4->MoveNext())
{
NetworkInterface^ adapter = safe_cast<NetworkInterface ^>(myEnum4->Current);
IPInterfaceProperties^ properties = adapter->GetIPProperties();
if (adapter->OperationalStatus == OperationalStatus::Up)
{
GatewayIPAddressInformationCollection^ gateways = properties->GatewayAddresses;
for each (GatewayIPAddressInformation^ gateway in gateways)
{
if (gateway->Address->AddressFamily == AddressFamily::InterNetwork)
{
defaultGateway = gateway->Address->ToString();
for each (UnicastIPAddressInformation^ unicastIPAddressInformation in properties->UnicastAddresses)
{
if (unicastIPAddressInformation->Address->AddressFamily == AddressFamily::InterNetwork)
{
interfaceIPAddress = unicastIPAddressInformation->Address->ToString();
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
}
}
}
}
}
}
Our web apps are currently in C# running on Windows and IIS. We rely heavily on the Windows authentication scheme that is included in this environment. With Windows authentication enabled we can detect the identity of the connected user and perform authorization on what screens and operation they are able to use.
If I set up a Phoenix web application will it be possible to detect the identity of the connected user based on their current Windows login? If not is there an easy to use replacement for the Windows authentication?
I just did this over the weekend. Yes, it is possible. You have to use the HttpPlatformHandler add-on for IIS to make it work. HttpPlatformHandler has the forwardWindowsAuthToken configuration setting that you can use to forward the Windows user token for the authenticated user from IIS to your Phoenix application which is running as a child process. You have to use NIFs to process the token and get the Windows username or SID. As you'll note from the docs, you need to call CloseHandle to release the Windows user token for each request.
(I apologize in advance if the code below is not up to best practices. I'm new to Elixir and am actively trying to learn how to write better code. This was also from a hacking session trying to figure out the solution this weekend, so it's also not necessarily polished either.)
To do this, I packaged everything into a custom plug that I could put into the pipeline (I removed Logger statements to compress the size of the example):
defmodule MyApp.WindowsAuthentication do
import Plug.Conn
require Logger
#on_load :load_nifs
def load_nifs do
if match? {:win32, _}, :os.type do
:erlang.load_nif("./priv/windows_authentication", 0)
else
:ok
end
end
def init(options), do: options
def call(conn, _options) do
if match? {:win32, _}, :os.type do
case get_req_header(conn, "x-iis-windowsauthtoken") do
[token_handle_string] ->
# token_handle_string is a hex string
token_handle = String.to_integer(token_handle_string, 16)
case do_get_windows_username(token_handle) do
{:ok, {domain_name, username}} ->
conn = assign(conn, :windows_user, {domain_name, username})
error ->
Logger.error IO.inspect(error)
end
do_close_handle(token_handle)
[] ->
Logger.debug "X-IIS-WindowsAuthToken was not present"
end
end
conn
end
def do_get_windows_username(_token_handle) do
raise "do_get_windows_username/1 is only available on Windows"
end
def do_close_handle(_handle) do
raise "do_close_handle/1 is only available on Windows"
end
end
The C source code for the NIFs is below:
#include <Windows.h>
#include <erl_nif.h>
static const char* error_atom = "error";
static const char* invalid_token_handle_atom = "invalid_token_handle";
static const char* ok_atom = "ok";
static const char* win32_error_atom = "win32_error";
#define MAX_NAME 256
static HANDLE get_user_token(ErlNifEnv *env, ERL_NIF_TERM token) {
HANDLE token_handle;
if (!enif_get_ulong(env, token, (unsigned long *)&token_handle)) {
return NULL;
}
return token_handle;
}
static ERL_NIF_TERM make_win32_error_tuple(ErlNifEnv* env, DWORD error_code) {
return enif_make_tuple2(
env,
enif_make_atom(env, error_atom),
enif_make_ulong(env, error_code)
);
}
static ERL_NIF_TERM make_invalid_token_handle_error(ErlNifEnv* env) {
return enif_make_tuple2(
env,
enif_make_atom(env, error_atom),
enif_make_atom(env, invalid_token_handle_atom)
);
}
static ERL_NIF_TERM do_get_windows_username(ErlNifEnv* env, int argc, ERL_NIF_TERM argv[]) {
HANDLE token_handle;
DWORD token_user_length;
PTOKEN_USER token_user;
DWORD last_error;
WCHAR username[MAX_NAME];
DWORD username_length = MAX_NAME;
WCHAR domain_name[MAX_NAME];
DWORD domain_name_length = MAX_NAME;
size_t converted_chars;
char converted_username[MAX_NAME * 2];
char converted_domain_name[MAX_NAME * 2];
errno_t err;
BOOL succeeded;
SID_NAME_USE sid_name_use;
token_handle = get_user_token(env, argv[0]);
if (!token_handle) {
return make_invalid_token_handle_error(env);
}
if (!GetTokenInformation(token_handle, TokenUser, NULL, 0, &token_user_length)) {
last_error = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER != last_error) {
return make_win32_error_tuple(env, last_error);
}
}
token_user = (PTOKEN_USER)malloc(token_user_length);
if (!GetTokenInformation(token_handle, TokenUser, token_user, token_user_length, &token_user_length)) {
free(token_user);
return make_win32_error_tuple(env, GetLastError());
}
succeeded = LookupAccountSidW(
NULL,
token_user->User.Sid,
username,
&username_length,
domain_name,
&domain_name_length,
&sid_name_use);
if (!succeeded) {
free(token_user);
return make_win32_error_tuple(env, GetLastError());
}
err = wcstombs_s(&converted_chars, converted_username, 512, username, username_length);
err = wcstombs_s(&converted_chars, converted_domain_name, 512, domain_name, domain_name_length);
free(token_user);
return enif_make_tuple2(
env,
enif_make_atom(env, ok_atom),
enif_make_tuple2(
env,
enif_make_string(env, converted_domain_name, ERL_NIF_LATIN1),
enif_make_string(env, converted_username, ERL_NIF_LATIN1)
)
);
}
static ERL_NIF_TERM do_close_handle(ErlNifEnv* env, int argc, ERL_NIF_TERM argv[]) {
HANDLE token_handle;
token_handle = get_user_token(env, argv[0]);
if (!token_handle) {
return make_invalid_token_handle_error(env);
}
if (!CloseHandle(token_handle)) {
return make_win32_error_tuple(env, GetLastError());
}
return enif_make_atom(env, ok_atom);
}
static ErlNifFunc nif_functions[] = {
{ "do_close_handle", 1, do_close_handle },
{ "do_get_windows_username", 1, do_get_windows_username }
};
ERL_NIF_INIT(
Elixir.MyApp.WindowsAuthentication,
nif_functions,
NULL,
NULL,
NULL,
NULL
)
You can compile the C code using the 64-bit Visual Studio C++ tools (open the x64 VS command prompt). I tried this out with the new VS2017 tools. Put the DLL in the priv directory of your application.
cl /LD /I "C:\Program Files\erl-8.2\erts-8.2\include" /DDEBUG windows_authentication.c advapi32.lib
To run the plug, add it to your pipeline in web/router.ex:
pipeline :browser do
plug :accepts, ["html"]
plug MyApp.WindowsAuthentication
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
The end result of this is that conn.assigns.windows_user will contain a tuple of the form {domain_name, username} that has the Windows domain username for the authenticated user.
Note: When I was trying this, I found CPU and memory leak issues from erl.exe when running as a child process of IIS. I'm still trying to figure that out in case you see it. I posted a question about it here.
I'll probably release this as a library on hex.pm when I've cleaned it up and fixed the memory/CPU issue, but for now, here's the code that will let you use Windows authentication with Phoenix.
I started learning some basics about OpenCL a while ago and decided to give the "Basic programming sample" from Apple a go. I runs OK on CPU, but when I select GPU as the target device I get err = -45 from
err = gclExecKernelAPPLE(k, ndrange, &kargs);
This error code translates to CL_INVALID_PROGRAM_EXECUTABLE. Any idea how can I correct the sample code?
Automatically generated kernel.cl.c code looks like this (+ includes on top):
static void initBlocks(void);
// Initialize static data structures
static block_kernel_pair pair_map[1] = {
{ NULL, NULL }
};
static block_kernel_map bmap = { 0, 1, initBlocks, pair_map };
// Block function
void (^square_kernel)(const cl_ndrange *ndrange, cl_float* input, cl_float* output) =
^(const cl_ndrange *ndrange, cl_float* input, cl_float* output) {
int err = 0;
cl_kernel k = bmap.map[0].kernel;
if (!k) {
initBlocks();
k = bmap.map[0].kernel;
}
if (!k)
gcl_log_fatal("kernel square does not exist for device");
kargs_struct kargs;
gclCreateArgsAPPLE(k, &kargs);
err |= gclSetKernelArgMemAPPLE(k, 0, input, &kargs);
err |= gclSetKernelArgMemAPPLE(k, 1, output, &kargs);
gcl_log_cl_fatal(err, "setting argument for square failed");
err = gclExecKernelAPPLE(k, ndrange, &kargs);
gcl_log_cl_fatal(err, "Executing square failed");
gclDeleteArgsAPPLE(k, &kargs);
};
// Initialization functions
static void initBlocks(void) {
const char* build_opts = " -cl-std=CL1.1";
static dispatch_once_t once;
dispatch_once(&once,
^{ int err = gclBuildProgramBinaryAPPLE("OpenCL/kernel.cl", "", &bmap, build_opts);
if (!err) {
assert(bmap.map[0].block_ptr == square_kernel && "mismatch block");
bmap.map[0].kernel = clCreateKernel(bmap.program, "square", &err);
}
});
}
__attribute__((constructor))
static void RegisterMap(void) {
gclRegisterBlockKernelMap(&bmap);
bmap.map[0].block_ptr = square_kernel;
}
I saw this same problem when running under 10.7.3, while a machine on 10.7.5 worked fine. I noticed the CVMCompiler process was crashing after each invocation of my app.
Inspecting the stack trace, I noticed it was crashing when trying to parse the bitcode for compilation into native code. Since the parsing of the bitcode failed failed, there was no resulting compiled program for gclExecKernelAPPLE() to execute, hence the error.
Try upgrading to 10.7.5, or indeed 10.8 and the problem should go away. (I just tested this and it does indeed fix the problem.)