A probable bug of external llvm GlobalVariable with TLS? - llvm

I'm trying to make a "thread-local" global variable in LLVM (MSVC, Windows).
Usually an external global variable can be initialized like this :
TheExecutionEngine->addGlobalMapping(module->getGlobalVariable("bsp"), &somevalue);
But when I create a TLS GlobalVariable (setting ThreadLocalModel=GlobalVariable::LocalDynamicTLSModel)
, addGlobalMapping seems not working.
Here is an example:
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
InitializeNativeTargetAsmParser();
llvm::LLVMContext& context = llvm::getGlobalContext();
llvm::Module *module = new llvm::Module("top", context);
llvm::IRBuilder<> builder(context);
GlobalVariable* bsp=new GlobalVariable(*module,Type::getInt32Ty(context),false,GlobalValue::ExternalLinkage,0,"bsp",0,GlobalVariable::LocalDynamicTLSModel);
llvm::FunctionType *funcType =
llvm::FunctionType::get(builder.getInt32Ty(), false);
llvm::Function *mainFunc =
llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", module);
llvm::BasicBlock *entry = llvm::BasicBlock::Create(context, "entrypoint", mainFunc);
builder.SetInsertPoint(entry);
//builder.CreateStore(ConstInt(32,123),bsp);
builder.CreateRet(builder.CreateLoad(bsp));
module->dump( );
ExecutionEngine *TheExecutionEngine;
long ou=1;
TheExecutionEngine = EngineBuilder(module).create();
module->getGlobalVariable("bsp")->dump();
TheExecutionEngine->addGlobalMapping(module->getGlobalVariable("bsp"),&ou);
typedef int (*PF)();
PF fun=(PF)TheExecutionEngine->getPointerToFunction(mainFunc);
printf("%d %d\n",ou,fun());
system("pause");
Here I create a function "main" in llvm which returns the value of a global variable bsp.
Before I jit the llvm module, I set a variable in C++ ("long ou=1;"), and map the llvm variable bsp to the C++ variable "ou"(TheExecutionEngine->addGlobalMapping(module->getGlobalVariable("bsp"), &ou);).
Finally I jit the function and run it. "fun()" is expected to return value 1, but it returns a random number.
I have tried my codes on llvm 3.4.2 with "old JIT" (not MCJIT)
I think the llvm global variable is not successfully mapped to the C++ variable.
Maybe I use thread-local global variable in a wrong way, or it is an llvm bug?

Your external variable is of long type, which is different from the type of external global variable you construct in IR

Related

How to resolve symbols that reside in the current session when constructing a (OrcV2) Jit compiler with llvm-13?

Edit
I'm basically trying to do this but with llvm's orc Jit api (llvm-13)
I have a library that JIT's some code using llvm (13). I have some functions in that library that I want to make available to the JIT without writing them in LLVM IR.
Here's some code:
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
#include "llvm/ExecutionEngine/Orc/Mangling.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/TargetSelect.h"
using namespace llvm;
using namespace llvm::orc;
// this is just a demo module that creates a function that adds 1 to an int
ThreadSafeModule makeSimpleModule() {
auto Context = std::make_unique<LLVMContext>();
auto M = std::make_unique<Module>("test", *Context);
// Create the add1 function entry and insert this entry into module M. The
// function will have a return type of "int" and take an argument of "int".
Function *Add1F =
Function::Create(FunctionType::get(Type::getInt32Ty(*Context),
{Type::getInt32Ty(*Context)}, false),
Function::ExternalLinkage, "add1", M.get());
// Add a basic block to the function. As before, it automatically inserts
// because of the last argument.
BasicBlock *BB = BasicBlock::Create(*Context, "EntryBlock", Add1F);
// Create a basic block builder with default parameters. The builder will
// automatically append instructions to the basic block `BB'.
IRBuilder<> builder(BB);
// Get pointers to the constant `1'.
Value *One = builder.getInt32(1);
// Get pointers to the integer argument of the add1 function...
assert(Add1F->arg_begin() != Add1F->arg_end()); // Make sure there's an arg
Argument *ArgX = &*Add1F->arg_begin(); // Get the arg
ArgX->setName("AnArg"); // Give it a nice symbolic name for fun.
// Create the add instruction, inserting it into the end of BB.
Value *Add = builder.CreateAdd(One, ArgX);
// Create the return instruction and add it to the basic block
builder.CreateRet(Add);
return {std::move(M), std::move(Context)};
}
// this represents a function in my library that I want to make available to the JIT.
namespace mylibsubnamespace {
extern "C" {
int add2(int a) {
return a + 2;
}
}
}
int main(int argc, const char *argv[]) {
// do some JIT initialization
llvm::InitLLVM X(argc, argv);
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
// Create an LLJIT instance.
auto J = LLJITBuilder().create();
// this code seems to enable symbol resolution for when the missing symbol is
// in the standard C library (and presumably included).
// This is what allows the "cos" function below to work (comment it out and we get a seg fault)
auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
(*J)->getDataLayout().getGlobalPrefix());
if (!DLSG) {
llvm::logAllUnhandledErrors(
std::move(DLSG.takeError()),
llvm::errs(),
"DynamicLibrarySearchGenerator not built successfully"
);
}
(*J)->getMainJITDylib().addGenerator(std::move(*DLSG));
auto M = makeSimpleModule();
(*J)->addIRModule(std::move(M));
// Look up the JIT'd function, cast it to a function pointer, then call it.
// This function is written in LLVM IR directly.
auto Add1Sym = (*J)->lookup("add1");
int (*Add1)(int) = (int (*)(int)) Add1Sym->getAddress();
int Result = Add1(42);
outs() << "add1(42) = " << Result << "\n";
// Look up the JIT'd function, cast it to a function pointer, then call it.
// This function is defined in the standard C library. Its symbol is resolved
// by DynamicLibrarySearchGenerator above
auto CosSym = (*J)->lookup("cos");
double (*Cos)(double) = (double (*)(double)) CosSym->getAddress();
outs() << "Cos(50) = " << Cos(50) << "\n";
So far so good. What I haven't been able to work out is how to make the add2 function available in a cachable way. I have successfully been able to follow the instructions here to enable hardcoding of the address in the current session, like so:
auto symbolStringPool = (*J)->getExecutionSession().getExecutorProcessControl().getSymbolStringPool();
orc::SymbolStringPtr symbPtr = symbolStringPool->intern("add2");
// JITTargetAddress is uint64 typedefd
llvm::JITSymbolFlags flg;
llvm::JITEvaluatedSymbol symb((std::int64_t) &mylibsubnamespace::add2, flg);
if (llvm::Error err = (*J)->getMainJITDylib().define(
llvm::orc::absoluteSymbols({{symbPtr, symb}}))) {
llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "Could not add symbol add2");
}
But the instructions explicitly advice against this strategy, since symbols resolved this way are not cachable. However, resolving the symbol with something like how the instructions suggest:
JD.addGenerator(DynamicLibrarySearchGenerator::Load("/path/to/lib"
DL.getGlobalPrefix()));
isn't possible because there is no /path/to/lib. What is the normal way to handle such situations?
What you need is to add -rdynamic or -Wl, -export-dynamic flag to the linker.
-E --export-dynamic
When creating a dynamically linked executable, add all symbols to the dynamic symbol table. The dynamic symbol table is the set of symbols which are visible from dynamic objects at run time. If you do not use this option, the dynamic symbol table will normally contain only those symbols which are referenced by some dynamic object mentioned in the link. If you use dlopen to load a dynamic object which needs to refer back to the symbols defined by the program, rather than some other dynamic object, then you will probably need to use this option when linking the program itself.

Rust, how to use global variable from DLL? C++ equivalent requires __declspec(dllimport)

Edit: After some research, I have found a partial solution. The link_name attribute can be used to change the name that is linked for an external variable. My question is now if this attribute can automatically be applied by bindgen to resolve linking errors. Context:
I am trying to get my Rust project to work with Julia's C library. I trimmed down my original error to this code:
// main.rs
extern "C" {
pub static mut jl_method_type: *mut ();
}
fn main() {
println!("{:?}", unsafe { jl_method_type });
}
// build.rs
fn main() {
println!("cargo:rustc-link-search=C:/path/to/julia/lib/");
println!("cargo:rustc-link-lib=julia");
}
(I also had to rename the file libjulia.dll.a to julia.lib so that the linker could find it.)
The error produced is as follows:
note: reprod.jth9vslxkrhc6ez.rcgu.o : error LNK2019:
unresolved external symbol jl_method_type referenced
in function _ZN1reprod4main17hf5ae7fcf7e25b2b0E
To try and trim it down further, I replicated it in C++:
// Compile with clang -ljulia -LC:/path/to/julia/lib/ test.cpp -IC:/path/to/julia/include/julia/
extern "C" void* jl_method_type;
int main() {
auto local = jl_method_type;
return 0;
}
This produces the same error as before. I then found this SO question and realized I needed to modify the definition like so:
extern "C" __declspec(dllimport) void* jl_method_type;
The C++ program then compiled without any errors. However, I was unable to find an equivalent for Rust.
Some additional notes, the actual code I am using to interface with Julia uses bindgen to generate the Rust bindings for the DLL. As far as I can tell from the linker errors, they work fine with the functions defined by the DLL but fail when trying to access global variables. This lines up with the problem in the linked question where adding __declspec() was optional on functions but required on variables.
Edit: I have found that using the name __impl_jl_method_type without the __declspec(dllimport) annotation results in a successful link. This also works in the Rust code. However, since the actual code I am using is generated by bindgen, it is missing this prefix. The code is also used by a crate that provides a safe wrapper around the bindings, so it seems like there should be a way to get everything to link correctly without having to change the variable names depending on the platform.
The ultimate solution turned out to be that Rust's #[link(name="libname", kind="dylib")] annotation functions like the __declspec(dllimport) annotation in C++. I added some code to my build script to automatically insert this annotation above every part where bindgen created bindings for a global variable:
let mut code = bindings.to_string();
if cfg!(target_os = "windows") {
code = code.replace(
"extern \"C\" {",
"#[link(name = \"julia\", kind = \"dylib\")]\r\nextern \"C\" {",
);
}
// Write the bindings to the $OUT_DIR/bindings.rs file.
let mut file = std::fs::File::create(&out_path).unwrap();
use std::io::Write;
file.write_all(code.as_bytes()).unwrap();
The result:
#[link(name="julia", kind="dylib")]
extern "C" {
pub static mut global_var: *mut some_bound_type_t;
}
Previous hacky solution in case any future people find it useful:
For now I have found a workaround by adding this to the build.rs file which generates the bindings:
let mut code = bindings.to_string();
if (cfg!(target_os = "windows")) {
const BEFORE_GLOBAL: &'static str = "extern \"C\" {\r\n pub static mut ";
let mut prev_index = 0;
while let Some(index) = code[prev_index..].find(BEFORE_GLOBAL) {
let index = index + prev_index;
let name_start = BEFORE_GLOBAL.len();
let colon = code[index..].find(":").expect("Invalid syntax.");
let name = &code[index..][name_start..colon];
let annotation = format!("#[link_name = \"__imp_{}\"]\r\n ", name);
code.insert_str(index + "extern \"C\" {\r\n ".len(), &annotation[..]);
prev_index = index;
}
}
It modifies the generated code so that global variables look like this:
extern "C" {
#[link_name = "__imp_jl_vararg_type"]
pub static mut jl_vararg_type: *mut jl_unionall_t;
}

'Attempt to index a string value' error while loading a c++ module in lua

I'm trying to use a function written in C++ from lua. Given below is the cpp file:
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
static int add_5(lua_State *L)
{
double d = lua_tonumber(L, 1); /* get argument */
d=d+5;
lua_pushnumber(L, d); /* push result */
return 1; /* number of results */
}
static const struct luaL_Reg mylib [] =
{
{"add_5", add_5},
{NULL, NULL} /* sentinel */
};
extern "C"
{
int luaopen_mylib (lua_State *L)
{
//luaL_newlib(L, mylib);
luaL_register(L, NULL, mylib);
return 1;
}
}
I compiled the above code by g++ using the following command:
g++ -shared -o mylib.so test.cpp -fPIC
I'm getting the following error on the lua interpreter:
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> local temp = require "mylib"
attempt to index a string value
stack traceback:
[C]: ?
[C]: in function 'require'
stdin:1: in main chunk
[C]: ?
Please note that I can't upgrade the version of Lua due to some reasons.
The second argument to luaL_register is the library name. You can leave it as NULL, but if you do, luaL_register will try to insert the registered functions into the table it expects to find on the top of the stack (and in your code there's no table on top of the stack). For the general case of registering a library, it's easiest to pass your library name as the second parameter.
Note that LHF suggests not doing it that way, since it automatically puts the libary's table into the global table, whereas the user of the library might want to have it only as a local variable. The alternative is to create your own table with lua_newtable before calling luaL_register (with a null name).

Undefined symbol when Calling function in C++ Random Char added

In NodeJS I'm building an interface to a shared object in C. I have the following code:
#include <node.h>
#include "libcustom_encryption.h"
namespace demo {
using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
//
// This is the implementation of the "add" method
// Input arguments are passed using the
// const FunctionCallbackInfo<Value>& args struct
//
void DeviceGetVersion(const FunctionCallbackInfo<Value>& args)
{
char ver[10] = {0};
unsigned int ver_size = 0;
device_get_version(ver, ver_size);
Isolate* isolate = args.GetIsolate();
//
// 1. Save the value in to a isolate thing
//
Local<Value> str = String::NewFromUtf8(isolate, "Test");
//
// 2. Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//
args.GetReturnValue().Set(str);
}
void Init(Local<Object> exports)
{
NODE_SET_METHOD(exports, "devicegetversion", DeviceGetVersion);
}
NODE_MODULE(addon, Init)
}
node-gyp configure: works
node-gyp build: works
LD_LIBRARY_PATH=libs/ node index.js: doesn't work
I get the following error:
node: symbol lookup error: /long_path/build/Release/app.node: undefined symbol: _Z18device_get_versionPcS_Phj
The when the function is called it gets prepended and appended with random characters. I'm assuming this is random data are some noise from memory. It seams as if the size brakes to call the function is bigger then it should.
I'm not that experienced with mixing C++ and C, I would love to get an explanation on what is happening hear.
Tech specs:
GCC Version: gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
NodeJS Version: v6.2.0
the function is called it gets prepended and appended with random characters
It is called
name mangling that happens in C++.
The actual error here is that compiled module can not link to function device_get_version().
Your possible actions:
add the implementation of device_get_version to your module
properly link this function
simply remove that line and the error will disappear
UPD.
device_get_version may actually be a C function which is treated as a C++ function (you can tell it by mangled name it has).
Make sure your function declared as
extern "C" {
void device_get_version(...);
}

is it possible to call any host c/c++ function from lua script by function address?

I have compilied console host program written on c/c++ (i don't have sources). Host program have support for lua scripts (probably using lua virtual machine). Host program load lua libraries
luaopen_base
luaopen_table
luaopen_string
luaopen_math
luaopen_debug
and allow reloading all lua scripts.
Is it possible to call any host c/c++ function from lua script by function address (got them from external debugger in host program)?
Is it possible to load any C/C++ compilied libs from lua in this case and call its functions?
One man on other forum wrote this code for this question
// re = callClientFunction(addr, { args }, 'cdecl')
// re = callClientFunction(method, { obj, arg1 }, 'this')
// re = callClientFunction(0x08, { obj, arg1 }, 'this') = obj->vtable[2]->(arg1)
inline int callCdeclFunction(lua::State* L, uintptr_t addr, const std::vector<lua::Integer>& args)
{
typedef lua::Integer __cdecl cf0();
typedef lua::Integer __cdecl cf1(lua::Integer);
typedef lua::Integer __cdecl cf2(lua::Integer, lua::Integer);
typedef lua::Integer __cdecl cf3(lua::Integer, lua::Integer, lua::Integer);
typedef lua::Integer __cdecl cf4(lua::Integer, lua::Integer, lua::Integer, lua::Integer);
typedef lua::Integer __cdecl cf5(lua::Integer, lua::Integer, lua::Integer, lua::Integer, lua::Integer);
lua::Integer re = 0;
switch(args.size())
{
case 0: re = reinterpret_cast<cf0*>(addr)(); break;
case 1: re = reinterpret_cast<cf1*>(addr)(args[0]); break;
case 2: re = reinterpret_cast<cf2*>(addr)(args[0], args[1]); break;
case 3: re = reinterpret_cast<cf3*>(addr)(args[0], args[1], args[2]); break;
case 4: re = reinterpret_cast<cf4*>(addr)(args[0], args[1], args[2], args[3]); break;
case 5: re = reinterpret_cast<cf5*>(addr)(args[0], args[1], args[2], args[3], args[4]); break;
default:
luaL_error(L, "%s: too many args (max %d, provided %d).\n", __func__, 5, args.size());
}
return re;
}
any ideas how use it in compilied host program?
The proper way to call C/C++ functions from Lua is to write interface code to exchange data on the Lua stack.
However there are extensions to allow direct call to functions in shared libraries (.dll or .so).
Have a look at the FFI Library (http://luajit.org/ext_ffi.html)
or Alien Lua (http://alien.luaforge.net/) which uses the libffi library (http://www.sourceware.org/libffi/)
To access C/C++ functions in Lua you have to have them exposed via a certain api, Lua does not load "regular" dlls (or .so if you which) directly. Instead you have to have a intermediate library to expose to the Lua environment which C functions will be available.
Cheers