Accessing lua_State from another application - c++

Now imagine you have two programs with different lua instances. One is the main program, the second is the dll you coded for it.
In my question, I will name the main program as main, dll i child from now on. We load the child into the Main process, detouring it and somehow accessing lua_State.
My main question is, can we do lua_pcall or dofile via the lua_State we grab while the main program is running?
Sample code
Main program:
#include <lua.hpp>
bool loadFile(lua_State* L) {
// run the Lua script
luaL_dofile(L, "helloworld.lua");
if (lua_pcall(L, 0, 0, eh) != 0)
{
std::string err = luaL_checkstring(L, -1);
lua_pop(L, 1);
}
}
int main()
{
// create new Lua state
lua_State *lua_state;
lua_state = luaL_newstate();
loadFile(lua_state);
}
Child program:
#include <lua.hpp>
#include "hookingLibrary.h"
typedef int(__fastcall* main_loadFile_Proto)(lua_State* L);
main_loadFile_Proto main_loadFile_Ptr;
lua_State * L lastState;
uint64_t main_loadFile_Addr = 0x0;
int main_loadFile_Detour(lua_State* L) {
lastState = L;
return main_loadFile_Ptr(L);
}
int main()
{
// detouring etc.
// I do not put detouring codes here. I am just declaring it as an
// opinion.
HookingLibrary::hook((LPVOID)(uintptr_t)main_loadFile_Addr, &main_loadFile_Detour, (LPVOID*)&main_loadFile_Ptr);
do{
Sleep(100);
}while(!lastState);
// create new Lua state
lua_State *lua_state;
lua_state = lastState;
// run the Lua script
luaL_dofile(lua_state, "helloworld.lua");
// close the Lua state
lua_close(lua_state);
}

Now imagine you have two programs with different lua instances. One is the main program, the second is the dll you coded for it.
This statement is not very clear, it depends on your expectations. I see 2 possible answers.
Create a DLL which implement some additional functions for Lua. The DLL library can be used later by the main program. In this case, there is only 1 instance of lua_state, there is only 1 Lua interpreter. This Lua interpreter may be created by the DLL or by the main function.
The interface of DLL is something like that:
#ifndef DLL_AUGMENTED
#define DLL_AUGMENTED
#include "lua.h"
lua_State *DLL_CreateAugmentedLuaInterpreter ();
void DLL_FreeLuaInterpreter ();
#endif
And can be used by main:
#include "lua-augmented.h"
int main (int argc, char **argv)
{
lua_State *LuaState = DLL_CreateAugmentedLuaInterpreter ();
// TODO: use the augmented Lua instance
DLL_FreeLuaInterpreter(LuaState);
return 0;
}
There is a need to have 2 Lua instances, as written One is the main program, the second is the dll. In this case, it's more difficult because a IPC Interprocess Communication need to be implemented with sockets or pipes. The best is to look for LuaSocket library.
Interprocess communication in Lua with Example?

Related

Lua: Cant open shared library when lua is wrapped in C++

EDIT: Nearly got the answer, I just dont completely understand it, see last paragraph.
I try to build a shared lua library and use it within a larger project. When calling the script which loads the shared library from shell everything works. However, when I wrap the script within another shell, I get a runtime error when loading the library. Dependent on the script it is just any call to a lua function from c (i.e. lua_pushnumber). Here is a minimal example.
totestlib.cpp:
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
int init(lua_State *L) {
lua_toboolean(L, -1);
return 0;
}
static const struct luaL_Reg testlib[] = {
{"init", init},
{NULL, NULL}
};
extern "C"
int luaopen_libtotestlib(lua_State *L) {
luaL_newlib(L, testlib);
return 1;
}
Compiled with: g++ -shared -fPIC -I./lua-5.4.4/src -L./lua-5.4.4/src totestlib.cpp -o libtotestlib.so
testlib.lua (testing shared library):
testlib.lua
print("start")
testlib = require("libtotestlib")
print("done")
testlib.init(true)
print("called")
Calling the lua script using ./lua-5.4.4/src/lua testlib.lua works. Everything is printed. Wrapping script in the following c++ code does not work:
call_testlib.cpp
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
#include <unistd.h>
static lua_State *L;
int main(int argc, char *argv[]) {
L = luaL_newstate();
luaL_openlibs(L);
int tmp = luaL_loadfile(L, "testlib.lua");
if(tmp != 0) {
return 1;
}
tmp = lua_pcall(L, 0, 0, 0);
if(tmp != 0) {
printf("error pcall\n");
return 1;
}
}
Compiled with g++ call_testlib.cpp -o ./call_testlib -I./lua-5.4.4/src -L./lua-5.4.4/src -llua it prints "error pcall". If I print the error message on the lua stack, I get:
string error loading module 'libtotestlib' from file './libtotestlib.so':
./libtotestlib.so: undefined symbol: luaL_checkversion_
In this case the undefined symbol is luaL_checkversion_ (which I dont call myself), but with other scripts it is usually the first lua_... function that I call.
I have tried several things to fix this. For example, linking -llua when compiling the shared library, but this does not work (and should not be the problem as calling the script itself works). I also tried to load preload the library from c++ (as done in this question) instead of from lua, but I guess it does not really make a difference and I am getting the same error. I also uninstalled all lua versions from my path to make sure I always use the same version.
What is the difference between calling the script directly from shell and calling it inside a c function? Am I doing something wrong?
EDIT: Nearly got the answer. When using MYCFLAGS= -fPIC when compiling lua I can link lua to the shared library. At least this one works, but does not seem like a good solution to me and does not really answer my question: Why can lua itself (from shell) somehow add these symbols to the library while the wrapped c version can not? Additionally, my program has lua once linked in the shared library and once in the compiled C++ project (not optimal imo).

How to distinguish files of two separate programs with common file

I've project where I need to distinguish files belongs to linux daemon (witten in C) and simple linux program (written in C++). Those two projects used 2 shared files (helpers_functions). Daemon and program has different logging system. Daemon write to file, program to stdout.
Problem occurs when I want to log something in common functions for both programs (inside helper_functions file). I don't want to pass via parameter, that this is program A, or program B.
I've compile files belongs to separate programs with g++ flag -D, but what can I do, when I want to log from common files? I cannot define there anything, because I don't know when I use it for program A, or when for program B.
You could add a global variable
const int iamprogram = ...;
which is defined to be PROGRAM_A in program A and PROGRAM_B in program B to solve the immediate problem. You could also make this variable directly contain the file you want to log to:
const char *program_logfile = "/path/to/logfileA";
In the long run, I suggest you to refactor your code such that the common code doesn't depend on which program it is part of. That's much more maintainable and expandable for the case where you want to use the code for a third program as well.
I'm not 100% sure if runtime dynamic linking can handle this. It would definitely work if you statically link the helper functions into each executable.
Provide a logging function with the same API in both programs. Have the library functions that want to log something call this function. They get the implementation provided by the program that's using the library.
Header file included by each program, and by the library
// common_log.h
#ifdef __cplusplus
extern "C" // for the following definition only, no opening {
#endif
// used by code that can be part of either program
void common_log(char *msg, int log_prio);
Implementation in the tty C++ program (simple logging):
#include "common_log.h"
#include <iostream>
// used by the rest of the C++ program
void simple_logger(char *msg) {
cerr << msg;
}
extern "C" void common_log(char *msg, int log_prio) {
simple_logger(msg);
}
Implementation in the daemon C program:
#include "common_log.h"
#include <stdio.h>
#include <errno.h>
static FILE *logfp;
static int log_level;
// used by daemon code
void fancy_logger(char *msg, int log_prio) {
if (log_prio < log_level)
return;
if (EOF == fputs(logfp, msg)) {
perror("failed to write log message to log file: ");
}
}
// or use linker tricks to make common_log an alias for fancy_log,
// if they both have the same signature and you don't need to do anything in the wrapper.
//extern "C" // this is already C
void common_log(char *msg, int log_prio) {
fancy_logger(msg, log_prio);
}
This requires the linker to be able to resolve undefined symbols in the library using symbols from the program that's linked against it. I think that works, similar to a library providing a weak definition of a global variable, so the main program's definition takes precedence.
If it was ok for simple_logger to also be extern "C" and have the same signature, you could just name them the same and avoid the bounce function. Or if the common function could be an alias for the program's own logging function in either of the programs, I think there are linker tricks to actually do that, instead of compiling to a single jmp instruction (tail-call optimization).
You could implement a callback for getting the program specific output. There's two benefits: no dependency from common part to application (common part defines the interface) and you can make the distinction at run time vs compile time, which gives more legroom for future development, such as changing the output via command line parameters or user interaction.
In the following example, let's refer to the common code part as "library".
library.h
typedef void (*logFunc_t)( logBuffer_t );
void setLogOutput( logFunc_t applicationLog );
library.c
logFunc_t logger; // might be good idea to initialize to an empty function, but omitted here
void setLogOutput( logFunc_t applicationLog )
{
logger = applicationLog;
}
void log( logBuffer_t data )
{
logger( data );
}
application.cpp / application.c
// here you should have the extern "C" in C++ application to ensure linkage compatibility
// I am assuming your shared code is C
extern "C" void myLogger( logBuffer_t data );
int main( int argc, char* agv[] )
{
setLogOutput( &myLogger );
// ...do your thing
return 0;
}
void myLogger( logBuffer_t data )
{
// ...log wherever
}

_wspawnl/_spawnl equivalent on Mac OS X or Linux

I am just porting a code to Mac OS X which is using _tspawnl on Windows.
Is there anything equivalent to _tspawnl on Mac OS X or Linux?
Or is there any posix equivalent to _tspawnl
You can use fork and execv system call together in the following way :
if (!fork()){ // create the new process
execl(path, *arg, ...); // execute the new program
}
The fork system call creates a new process, while the execv system call starts the execution of the application specify in path.
For example, you can use the following function spawn whose argument are the name of the application to be executed and the list of its arguments.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int spawn (char* program, char** arg_list)
{
pid_t child_pid;
/* Duplicate this process. */
child_pid = fork ();
if (child_pid != 0)
/* This is the parent process. */
return child_pid;
else {
/* Now execute PROGRAM, searching for it in the path. */
execvp (program, arg_list);
/* The execvp function returns only if an error occurs. */
fprintf (stderr, “an error occurred in execvp\n”);
abort ();
}
}
int main ()
{
/* The argument list to pass to the “ls” command. */
char* arg_list[] = {
“ls”, /* argv[0], the name of the program. */
“-l”,
“/”,
NULL /* The argument list must end with a NULL. */
};
spawn (“ls”, arg_list);
printf (“done with main program\n”);
return 0;
}
This example has been taken from the chapter 3.2.2 of this book. (Really good reference for development in Linux).
You can use fork()/exec(), as already pointed out, however a closer system call is posix_spawn() (manpage).
It can be a bit of a pain to set-up, however, but there is some example code using it is here (note that this code also provides functionality for Windows using the CreateProcess() API, which is probably what you should be using under Windows anyway).

Lua5.2 embedded in C++

I'm trying for the first time to have Lua embedded in C++. I've been searching for 2 days now, but most of internet tutos use lua5.1, which is incompatible with lua5.2. So I read a bit of lua documentation, example source code, and I end up with this :
main.cpp :
#include "luainc.h"
#include <iostream>
int main(){
int iErr = 0;
lua_State *lua = luaL_newstate (); // Open Lua
luaopen_io (lua); // Load io library
if ((iErr = luaL_loadfile (lua, "hw.lua")) == 0)
{
std::cout<<"step1"<<std::endl;
if ((iErr = lua_pcall (lua, 0, LUA_MULTRET, 0)) == 0)
{
std::cout<<"step2"<<std::endl;
lua_getglobal (lua, "helloWorld"); // Push the function name onto the stack
if (lua_type(lua, lua_gettop(lua)) == LUA_TNIL) {
// if the global variable does not exist then we will bail out with an error.
std::cout<<"global variable not found : helloworld"<<std::endl;
/* error so we will just clear the Lua virtual stack and then return
if we do not clear the Lua stack, we leave garbage that will cause problems with later
function calls from the application. we do this rather than use lua_error() because this function
is called from the application and not through Lua. */
lua_settop (lua, 0);
return -1;
}
// Function is located in the Global Table
/* lua_gettable (lua, LUA_GLOBALSINDEX); */ //lua5.1
lua_pcall (lua, 0, 0, 0);
}
}
lua_close (lua);
return 0;
}
hw.lua :
-- Lua Hello World (hw.lua)
function helloWorld ()
io.write ("hello World")
end
luainc.h :
#ifndef __LUA_INC_H__
#define __LUA_INC_H__
extern "C"
{
#include </home/renardc/Documents/Programmation/Lua_CPP/lua-5.2.2/src/lua.h>
#include </home/renardc/Documents/Programmation/Lua_CPP/lua-5.2.2/src/lauxlib.h>
#include </home/renardc/Documents/Programmation/Lua_CPP/lua-5.2.2/src/lualib.h>
}
#endif // __LUA_INC_H__
I have no error, the output is :
step1
step2
which should mean that my "helloworld" function has been found. But as I can not see "Hello World" in the output, I suspect that the function has not been called. What am I doing wrong ?
This is how I compile my program :
g++ main.cpp -L/usr/local/include -I/usr/local/include -llua
First, why not #include "lua.hpp", which comes with Lua and does mostly what your luainc.hdoes?
There are two problems with your code:
You don't emit any error message when luadL_loadfile fails.
You use lua_pcall to call helloWorld but does not test its return value.
When you change lua_pcall to lua_call you get this error message:
attempt to index global 'io' (a nil value)
This means that you forgot to set the global io after calling luaopen_io. Just add lua_setglobal(lua,"io")and it works. Unlike Lua 5.1, Lua 5.2 does not set globals automatically when you open libraries, unless the library itself does it, which is discouraged.
You'll probably be better off calling luaL_openlibs to open all standard Lua libraries with no surprises.
You may as well use luaL_dofile instead of luaL_loadfile and save the first lua_pcall. You still have to check the return value.

Sharing data with a dynamically loaded library (dlopen,dlsym)

My main program would load a simple dynamic library called hello.so
In main
void* handle = dlopen("./hello.so", RTLD_LAZY);
In main , pass a callback function called testing (defined somewhere in main.h) and invoke the hello() from the dynamic library
typedef void (*callback)();
typedef void (*hello_t)( callback);
/* do something */
hello_t hello = (hello_t) dlsym(handle, "hello");
hello(testing);
In dynamic library,
#include
#include "main.h"
extern "C" void hello( void (*fn)() ) {
/*do something and then invoke callback function from main */ fn();
}
Are there other ways to allow functions/data of main to be called/used from dynamic library apart from using callbacks?
No, this is the preferred way of doing it, in my opinion. Any other way that I can think of involves making the DLL aware of the objects in the program it's linked with, which is most likely bad practice.
Regarding data, just a reminder though you didn't ask, it's usually best practice to copy any data that needs to be stored, if it's passed across library/program boundaries. You can get into a complete mess if you have the library using data whose lifetime is controlled by the program, and vice versa.