mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-03 12:23:25 +00:00
Compiler driver wrapper for building pe-powerpcle executables. Expects access
to a powerpc gcc and ld, as well as alink. The scripts in here are transformed by sed ala GNU configure. fork_win32 and fork_unix provide a uniform way of calling an external program and reading stderr. dlltool.cpp is a minimalist what-if about replacing binutils' dlltool. It's not used by might be instructive. svn path=/branches/powerpc/; revision=25303
This commit is contained in:
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=@PREFIX@
|
||||
BPREFIX=@BPREFIX@
|
||||
TARGET=@TARGET@
|
||||
|
||||
${BPREFIX}dlltool -S $DIR/bin/${TARGET}as -L $DIR/bin/${TARGET}ld $*
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=@PREFIX@
|
||||
CPREFIX=@CPREFIX@
|
||||
TARGET=@TARGET@
|
||||
|
||||
if [ "x$*" = "x-v" ] ; then
|
||||
exec ${CPREFIX}gcc -v
|
||||
fi
|
||||
|
||||
includes="-isystem $DIR/lib/mingw-crt-headers -isystem $DIR/lib/gcc-core-headers"
|
||||
|
||||
argstr=""
|
||||
for arg in $* ; do
|
||||
if [ x"$arg" = x-mingw ] ; then
|
||||
argstr="$argstr -isystem $DIR/lib/windows-headers"
|
||||
else
|
||||
argstr="$argstr $arg"
|
||||
fi
|
||||
done
|
||||
|
||||
TEMP=`mktemp -d`
|
||||
export TEMP
|
||||
if [ ! -d $TEMP ] ; then
|
||||
echo "Could not make temp dir $TEMP"
|
||||
exit 1
|
||||
fi
|
||||
TMP=$TEMP
|
||||
|
||||
`dirname $0`/${TARGET}driver-gcc \
|
||||
-mlittle \
|
||||
-nostdinc \
|
||||
-D__cdecl__= \
|
||||
-D__stdcall__= \
|
||||
-D__fastcall__= \
|
||||
-D__declspec\(x\)= \
|
||||
-D__cdecl= \
|
||||
-D__stdcall= \
|
||||
-D__fastcall= \
|
||||
-Dcdecl= \
|
||||
-Dstdcall= \
|
||||
-Dfastcall= \
|
||||
-Ddllimport= \
|
||||
-Ddllexport= \
|
||||
-Du_short=unsigned\ short \
|
||||
-Du_long=unsigned\ long \
|
||||
-Du_int=unsigned\ int \
|
||||
-Du_char=unsigned\ char \
|
||||
-ldscript ${DIR}/lib/ldscript \
|
||||
-gcc-name ${CPREFIX}gcc \
|
||||
-link-stage-name ${DIR}/bin/${TARGET}ld \
|
||||
-extra-link-stage $DIR/bin/alink.exe \
|
||||
-mingw-lib-dir $DIR/lib \
|
||||
$includes \
|
||||
$argstr </dev/null
|
||||
|
||||
RES=$?
|
||||
|
||||
rm -rf $TEMP
|
||||
exit $RES
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=@PREFIX@
|
||||
BPREFIX=@BPREFIX@
|
||||
TARGET=@TARGET@
|
||||
|
||||
if [ "x$*" = x-v ] ; then
|
||||
exec ${BPREFIX}ld -v
|
||||
fi
|
||||
|
||||
TEMP=`mktemp -d`
|
||||
export TEMP
|
||||
if [ ! -d $TEMP ] ; then
|
||||
echo "Could not make temp dir $TEMP"
|
||||
exit 1
|
||||
fi
|
||||
TMP=$TEMP
|
||||
|
||||
`dirname $0`/${TARGET}driver-ld \
|
||||
-EL \
|
||||
-ldscript ${DIR}/lib/ldscript \
|
||||
-mingw-lib-dir ${DIR}/lib \
|
||||
-link-stage-name ${BPREFIX}ld \
|
||||
-extra-link-stage $DIR/bin/alink.exe \
|
||||
$*
|
||||
|
||||
RES=$?
|
||||
|
||||
rm -rf $TEMP
|
||||
exit $RES
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=@PREFIX@
|
||||
BPREFIX=@BPREFIX@
|
||||
TARGET=@TARGET@
|
||||
|
||||
exec ${BPREFIX}windres -F elf32-powerpcle $*
|
||||
@@ -0,0 +1,363 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include "fork_execvp.h"
|
||||
|
||||
/* tool for transforming gcc -### output and using it to drive the compiler
|
||||
* elements ourselves. Most importantly, we need to know what file gcc asks
|
||||
* the linker to input and output, and add an intermediate stage with alink.
|
||||
*/
|
||||
|
||||
typedef enum { empty, found_q, found_bs } break_t;
|
||||
|
||||
template <class T>
|
||||
T break_arguments( const std::string &line ) {
|
||||
break_t state = empty;
|
||||
T args;
|
||||
std::string arg;
|
||||
|
||||
for( size_t i = 0; i < line.size(); i++ ) {
|
||||
switch( state ) {
|
||||
case empty:
|
||||
if( line[i] == '\"' ) {
|
||||
state = found_q;
|
||||
}
|
||||
break;
|
||||
|
||||
case found_q:
|
||||
if( line[i] == '\\' ) state = found_bs;
|
||||
else if( line[i] == '\"' ) {
|
||||
state = empty;
|
||||
args.push_back(arg);
|
||||
arg = "";
|
||||
} else arg += line[i];
|
||||
break;
|
||||
|
||||
case found_bs:
|
||||
state = found_q;
|
||||
arg += line[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<char *> get_arg_ptrs
|
||||
( const T &arg_strings ) {
|
||||
std::vector<char *> arg_out;
|
||||
for( size_t i = 0; i < arg_strings.size(); i++ )
|
||||
arg_out.push_back( (char *)arg_strings[i].c_str() );
|
||||
arg_out.push_back(0);
|
||||
return arg_out;
|
||||
}
|
||||
|
||||
int execute_command( bool verbose, const std::vector<std::string> &args ) {
|
||||
std::string error;
|
||||
std::vector<char *> args_ptrs;
|
||||
const char *tag = "executable";
|
||||
|
||||
if( verbose ) {
|
||||
fprintf( stderr, "<command>\n" );
|
||||
for( size_t i = 0; i < args.size(); i++ ) {
|
||||
fprintf( stderr, "<%s>%s</%s>\n", tag, args[i].c_str(), tag );
|
||||
tag = "argument";
|
||||
}
|
||||
}
|
||||
|
||||
Process p = fork_execvp( args );
|
||||
|
||||
while( p && p->ProcessStarted() && !p->EndOfStream() )
|
||||
fprintf( stderr, "%s", p->ReadStdError().c_str() );
|
||||
|
||||
if( verbose ) {
|
||||
fprintf( stderr, "<status>%d</status>\n", p->GetStatus() );
|
||||
fprintf( stderr, "</command>\n" );
|
||||
}
|
||||
|
||||
return p ? p->GetStatus() : -1;
|
||||
}
|
||||
|
||||
std::string make_tmp_name() {
|
||||
int fd;
|
||||
std::string name;
|
||||
|
||||
while( true ) {
|
||||
name = tmpnam(NULL);
|
||||
name += ".obj";
|
||||
if( (fd = creat( name.c_str(), 0644 )) != -1 ) {
|
||||
close(fd);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recognize_arg( std::vector<std::string> &args,
|
||||
std::string &result,
|
||||
size_t &i,
|
||||
const std::string &argname,
|
||||
const std::string &short_argname = "" ) {
|
||||
if( (short_argname.size() && (args[i] == short_argname)) ||
|
||||
(args[i] == argname) ) {
|
||||
result = args[i+1];
|
||||
args.erase(args.begin()+i);
|
||||
args.erase(args.begin()+i--);
|
||||
} else if( args[i].substr(0,argname.size()+1) == argname + "=" ) {
|
||||
result = args[i].substr(argname.size()+1);
|
||||
args.erase(args.begin()+i--);
|
||||
}
|
||||
}
|
||||
|
||||
int run_ld( bool verbose, bool nostdlib, bool nostartfiles, bool is_dll,
|
||||
bool make_map,
|
||||
const std::string &lib_dir,
|
||||
const std::string &extra_linker_stage,
|
||||
const std::string &ldscript,
|
||||
const std::vector<std::string> &arg_vect ) {
|
||||
bool use_libgcc = false;
|
||||
std::vector<std::string> args = arg_vect;
|
||||
std::string temp_name = make_tmp_name(), real_output,
|
||||
entry_point, image_base, subsystem = "windows",
|
||||
make_dll, file_align, section_align, base_file;
|
||||
std::vector<std::string>::iterator i =
|
||||
std::find(args.begin(),args.end(),"-lgcc");
|
||||
|
||||
if( i != args.end() ) {
|
||||
args.erase( i );
|
||||
use_libgcc = true;
|
||||
}
|
||||
|
||||
if( !nostartfiles ) {
|
||||
if( make_dll.size() )
|
||||
args.push_back(lib_dir + "/dllcrt2.o");
|
||||
else
|
||||
args.push_back(lib_dir + "/crt2.o");
|
||||
}
|
||||
|
||||
if( !nostdlib ) {
|
||||
args.push_back(std::string("-L") + lib_dir);
|
||||
args.push_back("-lkernel32");
|
||||
args.push_back("-lmsvcrt");
|
||||
args.push_back("-lcrtdll");
|
||||
args.push_back("-lmingw32");
|
||||
}
|
||||
if( use_libgcc )
|
||||
args.push_back(lib_dir + "/libgcc.a");
|
||||
|
||||
if( verbose )
|
||||
args.insert(args.begin()+1,"-v");
|
||||
|
||||
args.insert(args.begin()+1,"-T");
|
||||
args.insert(args.begin()+2,ldscript);
|
||||
args.insert(args.begin()+1,"-r");
|
||||
args.insert(args.begin()+1,"--start-group");
|
||||
args.push_back("--end-group");
|
||||
|
||||
for( size_t i = 0; i < args.size(); i++ ) {
|
||||
if( args[i] == "-o" && i < args.size()-1 ) {
|
||||
real_output = args[++i];
|
||||
args[i] = temp_name;
|
||||
} else if( args[i].substr(0,4) == "-mdll" ) {
|
||||
args.erase(args.begin()+i--);
|
||||
make_dll = "-dll";
|
||||
}
|
||||
|
||||
recognize_arg( args, entry_point, i, "--entry", "-e" );
|
||||
recognize_arg( args, image_base, i, "--image-base" );
|
||||
recognize_arg( args, subsystem, i, "--subsystem" );
|
||||
recognize_arg( args, file_align, i, "--file-alignment" );
|
||||
recognize_arg( args, section_align, i, "--section-alignment" );
|
||||
recognize_arg( args, base_file, i, "--base-file" );
|
||||
}
|
||||
|
||||
if( execute_command( verbose, args ) )
|
||||
return 1;
|
||||
|
||||
if( base_file.size() ) {
|
||||
FILE *f = fopen(base_file.c_str(), "wb");
|
||||
if( !f ) {
|
||||
fprintf(stderr, "<error>\n");
|
||||
perror(base_file.c_str());
|
||||
fprintf(stderr, "</error>\n");
|
||||
return 1;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
args.clear();
|
||||
args.push_back( extra_linker_stage );
|
||||
if(make_map)
|
||||
args.push_back("-m+");
|
||||
args.push_back( "-oPE" );
|
||||
args.push_back( "-o" );
|
||||
args.push_back( real_output );
|
||||
args.push_back( "-subsys" );
|
||||
args.push_back( subsystem );
|
||||
|
||||
if( entry_point.size() ) {
|
||||
size_t at = 0;
|
||||
args.push_back("-entry");
|
||||
// Entry points will be specified with leading '_', probably
|
||||
if( entry_point[0] == '_' )
|
||||
entry_point = entry_point.substr(1);
|
||||
at = entry_point.find('@');
|
||||
if( at != std::string::npos )
|
||||
entry_point = entry_point.substr(0,at);
|
||||
args.push_back(entry_point);
|
||||
}
|
||||
|
||||
if( image_base.size() ) {
|
||||
args.push_back("-base");
|
||||
args.push_back(image_base);
|
||||
}
|
||||
|
||||
if( file_align.size() ) {
|
||||
args.push_back("-filealign");
|
||||
args.push_back(file_align);
|
||||
}
|
||||
|
||||
if( section_align.size() ) {
|
||||
args.push_back("-objectalign");
|
||||
args.push_back(section_align);
|
||||
}
|
||||
|
||||
if( make_dll.size() ) {
|
||||
args.push_back(make_dll);
|
||||
}
|
||||
|
||||
if( real_output.size() ) {
|
||||
args.push_back( temp_name );
|
||||
int res = execute_command( verbose, args );
|
||||
unlink( temp_name.c_str() );
|
||||
return res;
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
int main( int argc, char **argv ) {
|
||||
bool verbose = false, ld_mode = false, nostdlib = false,
|
||||
nostartfiles = false, is_dll = false, make_map = false;
|
||||
int err_fd[2], read_len, child_pid_gcc, child_pid_command, status = 0;
|
||||
std::string gcc_name, gcc_hash_output, gcc_line, linker_name = "ld",
|
||||
mingw_lib_dir, ldscript;
|
||||
std::string extra_linker_stage;
|
||||
std::vector<std::string> gcc_args_str,
|
||||
subcmd_args;
|
||||
std::vector<char *> arguments_for_gcc;
|
||||
char buf[1024];
|
||||
|
||||
for( int i = 1; i < argc; i++ ) {
|
||||
if( std::string("-gcc-name") == argv[i] && i < argc-1 ) {
|
||||
gcc_name = argv[++i];
|
||||
} else if( std::string("-ldscript") == argv[i] && i < argc-1 ) {
|
||||
ldscript = argv[++i];
|
||||
} else if( std::string("-link-stage-name") == argv[i] && i < argc-1 ) {
|
||||
linker_name = argv[++i];
|
||||
} else if( std::string("-extra-link-stage") == argv[i] && i < argc-1 ) {
|
||||
extra_linker_stage = argv[++i];
|
||||
} else if( std::string("-mingw-lib-dir") == argv[i] && i < argc-1 ) {
|
||||
mingw_lib_dir = argv[++i];
|
||||
} else if( std::string("-v") == argv[i] ) {
|
||||
verbose = true;
|
||||
} else if( std::string("-pipe") == argv[i] ) {
|
||||
/* ignore */
|
||||
} else if( std::string("-T") == argv[i] ) {
|
||||
/* ignore */
|
||||
i++;
|
||||
} else if( std::string("-nostdlib") == argv[i] ) {
|
||||
nostdlib = true;
|
||||
} else if( std::string("-nostartfiles") == argv[i] ) {
|
||||
nostartfiles = true;
|
||||
} else if( std::string("-shared") == argv[i] ) {
|
||||
is_dll = true;
|
||||
} else if( std::string("-map") == argv[i] ) {
|
||||
make_map = true;
|
||||
} else {
|
||||
gcc_args_str.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* We never use the system start files or standard libs */
|
||||
gcc_args_str.insert(gcc_args_str.begin()+1,"-nostdlib");
|
||||
gcc_args_str.insert(gcc_args_str.begin()+1,"-nostartfiles");
|
||||
|
||||
if( std::string(argv[0]).find("ld") != std::string::npos ) {
|
||||
gcc_args_str.insert
|
||||
( gcc_args_str.begin(), linker_name );
|
||||
return run_ld
|
||||
( verbose, nostdlib, nostartfiles, is_dll, make_map, mingw_lib_dir,
|
||||
extra_linker_stage, ldscript, gcc_args_str );
|
||||
}
|
||||
if( verbose ) fprintf( stderr, "<compiler-driver>\n" );
|
||||
|
||||
// Stack on driver name and dump commands flag
|
||||
gcc_args_str.insert(gcc_args_str.begin(),std::string("-###"));
|
||||
gcc_args_str.insert(gcc_args_str.begin(),gcc_name);
|
||||
|
||||
/* Redirect stderr to our pipe */
|
||||
if( verbose ) {
|
||||
const char *tag = "executable";
|
||||
fprintf( stderr, "<gcc>\n" );
|
||||
for( size_t i = 0; i < gcc_args_str.size(); i++ ) {
|
||||
fprintf( stderr, " <%s>%s</%s>\n",
|
||||
tag, gcc_args_str[i].c_str(),
|
||||
tag );
|
||||
tag = "arg";
|
||||
}
|
||||
fprintf( stderr, "</gcc>\n" );
|
||||
}
|
||||
|
||||
Process p = fork_execvp( gcc_args_str );
|
||||
|
||||
while( p && p->ProcessStarted() && !p->EndOfStream() )
|
||||
gcc_hash_output += p->ReadStdError();
|
||||
|
||||
std::istringstream iss( gcc_hash_output );
|
||||
|
||||
if( p->GetStatus() ) goto final;
|
||||
|
||||
while( std::getline( iss, gcc_line, '\n' ) ) {
|
||||
// command line
|
||||
if( gcc_line.size() > 2 && gcc_line[0] == ' ' ) {
|
||||
subcmd_args =
|
||||
break_arguments<std::vector<std::string> >( gcc_line );
|
||||
|
||||
if( subcmd_args.size() < 1 ) continue;
|
||||
|
||||
if( subcmd_args[0].find("collect2") != std::string::npos ||
|
||||
subcmd_args[0].find("ld") != std::string::npos ) {
|
||||
if( run_ld( verbose, nostdlib, nostartfiles, is_dll, make_map,
|
||||
mingw_lib_dir, extra_linker_stage, ldscript,
|
||||
subcmd_args ) )
|
||||
goto final;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
if( verbose )
|
||||
subcmd_args.insert(subcmd_args.begin()+1,"-v");
|
||||
|
||||
if( execute_command
|
||||
( verbose, subcmd_args ) )
|
||||
goto final;
|
||||
} else if( verbose )
|
||||
fprintf( stderr, "<error>%s</error>\n", gcc_line.c_str() );
|
||||
}
|
||||
goto theend;
|
||||
|
||||
final:
|
||||
status = 1;
|
||||
|
||||
theend:
|
||||
if( verbose ) {
|
||||
fprintf( stderr, "<status>%d</status>\n", status );
|
||||
fprintf( stderr, "</compiler-driver>\n" );
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,566 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
/*
|
||||
* .idata$2_liba_dll # liba import directory
|
||||
* __dll_liba_import_directory:
|
||||
* .rva __dll_tail
|
||||
* .long 0
|
||||
* .long 0
|
||||
* .rva __liba_dll_name
|
||||
* .rva __liba_dll_fthunk
|
||||
* .idata$2_libb_dll # libb import directory
|
||||
* __dll_libb_import_directory:
|
||||
* 20 bytes ...
|
||||
* .idata$3 # Zero entry
|
||||
* __dll_tail:
|
||||
* 20 zeroes ...
|
||||
* .idata$4_liba_dll # Import Lookup Table
|
||||
* __dll_liba_ilt:
|
||||
* .idata$4_liba_fun1_ilt
|
||||
* __dll_liba_fun1_ilt:
|
||||
* .rva __dll_liba_fun1_hint
|
||||
* .idata$4_liba_fun2_ilt
|
||||
* __dll_libb_fun2_ilt:
|
||||
* .rva __dll_liba_fun1_hint
|
||||
* .idata$4_libb_dll # Import Lookup Table
|
||||
* .idata$5_libb_fun1_ilt
|
||||
* .idata$5_libb_fun2_ilt
|
||||
* .idata$5_liba_dll # Import Address Table
|
||||
* .idata$5_liba_fun1_iat
|
||||
* __dll_liba_fun1_ia:
|
||||
* .long 0
|
||||
* .idata$5_liba_fun2_iat
|
||||
* .idata$5_libb_dll # Import Address Table
|
||||
* .idata$5_libb_fun1_iat
|
||||
* .idata$5_libb_fun2_iat
|
||||
* .idata$6_liba_dll # Hint Table
|
||||
* .idata$6_liba_fun1_hint
|
||||
* .idata$6_liba_fun2_hint
|
||||
* .idata$6_libb_dll # Hint Table
|
||||
* .idata$6_liba_fun1_hint
|
||||
* .idata$6_liba_fun2_hint
|
||||
* .idata$7_liba_dll # DLL Name
|
||||
* __liba_dll_name:
|
||||
* .asciz "liba.dll"
|
||||
* .idata$7_libb_dll # DLL Name
|
||||
* __libb_dll_name:
|
||||
* .asciz "libb.dll"
|
||||
*
|
||||
*/
|
||||
|
||||
std::string uppercase( const std::string &mcase ) {
|
||||
std::string out;
|
||||
for( std::string::const_iterator i = mcase.begin();
|
||||
i != mcase.end();
|
||||
i++ ) {
|
||||
out += toupper(*i);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string comment_strip( const std::string &line ) {
|
||||
size_t s = line.find(';');
|
||||
if( s != std::string::npos ) return line.substr(0,s);
|
||||
else return line;
|
||||
}
|
||||
|
||||
class DefFile {
|
||||
public:
|
||||
class Export {
|
||||
public:
|
||||
Export( const std::string &name, const std::string &alias = "",
|
||||
int ordinal = -1 ) :
|
||||
name(name), alias_of(alias), ordinal(ordinal) {}
|
||||
|
||||
const std::string &getName() const { return name; }
|
||||
const std::string &getAliasOf() const { return alias_of; }
|
||||
int getOrdinal() const { return ordinal; }
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string alias_of;
|
||||
int ordinal;
|
||||
};
|
||||
|
||||
DefFile() : ordbase(1), mode(0) { }
|
||||
|
||||
static std::string clean_for_symbol( const std::string &s ) {
|
||||
std::string out;
|
||||
|
||||
for( size_t i = 0; i < s.size(); i++ ) {
|
||||
out += isalpha(s[i]) ? s[i] : '_';
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::string section( const std::string &name ) {
|
||||
return std::string("\t.section\t") + name + "\n";
|
||||
}
|
||||
|
||||
static std::string symbol( const std::string &name ) {
|
||||
return name + ":\n";
|
||||
}
|
||||
|
||||
static std::string global( const std::string &name ) {
|
||||
return std::string("\t.global\t") + name + "\n";
|
||||
}
|
||||
|
||||
static std::string global_sym( const std::string &name ) {
|
||||
return global(name) + symbol(name);
|
||||
}
|
||||
|
||||
static std::string impsym( const std::string &name, size_t i ) {
|
||||
std::ostringstream oss;
|
||||
oss << name << "_" << i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string archive_deco( const std::string &sym ) {
|
||||
return std::string("__dll_archive_") + libsym + "_" + sym;
|
||||
}
|
||||
|
||||
static std::string rva( const std::string &symbol ) {
|
||||
return std::string("\t.rva\t") + symbol + "\n";
|
||||
}
|
||||
|
||||
std::string common_name( size_t i, const std::string &_name = "" ) const {
|
||||
size_t at;
|
||||
std::string name = _name;
|
||||
|
||||
if( !name.size() ) name = exports[i].getName();
|
||||
if( !name.size() ) {
|
||||
std::ostringstream oss;
|
||||
oss << libsym << "_ordinal_" << i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
if( name[0] == '@' ) name = name.substr(1);
|
||||
at = name.find('@');
|
||||
if( at != std::string::npos )
|
||||
name = name.substr(0,at);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
size_t count_names() const {
|
||||
size_t res = 0;
|
||||
for( size_t i = 0; i < exports.size(); i++ )
|
||||
if( exports[i].getName().size() ) ++res;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool parse( int ln, const std::string &line ) {
|
||||
std::string command;
|
||||
std::string stripped_line = comment_strip(line);
|
||||
|
||||
std::istringstream iss(stripped_line);
|
||||
|
||||
bool got_word = iss >> command;
|
||||
|
||||
if( uppercase(command) == "LIBRARY" ) {
|
||||
if( mode ) {
|
||||
fprintf( stderr, "Got library out of turn on line %d\n", ln );
|
||||
return false;
|
||||
}
|
||||
mode++;
|
||||
iss >> libname;
|
||||
libsym = clean_for_symbol(libname);
|
||||
import_directory_entry = archive_deco("import_directory_entry");
|
||||
import_directory_term = archive_deco("import_directory_term");
|
||||
dll_name_text = archive_deco("dll_name");
|
||||
original_first_thunk = archive_deco("ofirst_thk");
|
||||
original_first_thunk_term = original_first_thunk + "_term";
|
||||
first_thunk = archive_deco("first_thk");
|
||||
first_thunk_term = first_thunk + "_term";
|
||||
import_desc_table = archive_deco("import_desc");
|
||||
} else if( uppercase(command) == "EXPORTS" ) {
|
||||
if( !mode ) {
|
||||
fprintf( stderr, "Got exports out of turn on line %d\n", ln );
|
||||
return false;
|
||||
}
|
||||
mode++;
|
||||
} else if( got_word ) {
|
||||
size_t equal, at;
|
||||
std::string name, alias, name_alias, ordinal_maybe;
|
||||
int ordinal = -1;
|
||||
|
||||
if( mode != 2 ) {
|
||||
fprintf( stderr, "Got extraneous input on line %d\n", ln );
|
||||
return false;
|
||||
}
|
||||
|
||||
name_alias = command;
|
||||
if( iss >> ordinal_maybe )
|
||||
ordinal = atoi(ordinal_maybe.substr(1).c_str());
|
||||
|
||||
equal = name_alias.find('=');
|
||||
if( equal == std::string::npos ) {
|
||||
name = name_alias;
|
||||
} else {
|
||||
name = name_alias.substr(0,equal);
|
||||
equal++;
|
||||
alias = name_alias.substr(equal);
|
||||
}
|
||||
|
||||
if( kill_at ) {
|
||||
at = name.find('@');
|
||||
if( at != std::string::npos )
|
||||
name = name.substr(0,at);
|
||||
at = alias.find('@');
|
||||
if( at != std::string::npos )
|
||||
alias = alias.substr(0,at);
|
||||
}
|
||||
|
||||
Export ex(name,alias,ordinal);
|
||||
|
||||
export_byname.insert( std::make_pair(name, exports.size()) );
|
||||
exports.push_back(ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string makeExportData() const {
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << section(".edata")
|
||||
<< longdata(0)
|
||||
<< longdata(time(NULL))
|
||||
<< longdata(0)
|
||||
<< rva(libsym)
|
||||
<< longdata(ordbase)
|
||||
<< longdata(exports.size())
|
||||
<< longdata(count_names())
|
||||
<< rva("exported_functions")
|
||||
<< rva("exported_names")
|
||||
<< rva("exported_ordinals")
|
||||
<< global_sym(libsym)
|
||||
<< asciz(libname)
|
||||
<< align(4)
|
||||
<< symbol("exported_functions");
|
||||
|
||||
for( size_t i = 0; i < exports.size(); i++ ) {
|
||||
oss << "\t.rva\t" << common_name(i) << "\n";
|
||||
}
|
||||
|
||||
oss << "exported_ordinals:\n";
|
||||
int lastord = ordbase;
|
||||
for( size_t i = 0; i < exports.size(); i++ ) {
|
||||
if( exports[i].getOrdinal() != -1 )
|
||||
lastord = exports[i].getOrdinal();
|
||||
else
|
||||
lastord++;
|
||||
oss << "\t.short\t" << lastord << "\n";
|
||||
}
|
||||
|
||||
oss << "exported_names:\n";
|
||||
|
||||
for( size_t i = 0; i < exports.size(); i++ ) {
|
||||
if( exports[i].getName().size() ) {
|
||||
oss << "\t.rva\texport_name_" << (int)i << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < exports.size(); i++ ) {
|
||||
if( exports[i].getName().size() ) {
|
||||
oss << "export_name_" << (int)i
|
||||
<< ":\t.asciz\t\""
|
||||
<< exports[i].getName() << "\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
oss << "# End of exports\n";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::string align(int n) {
|
||||
std::ostringstream oss;
|
||||
oss << "\t.align\t" << n << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::string longdata(long l) {
|
||||
std::ostringstream oss;
|
||||
oss << "\t.long\t" << l << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::string shortdata(int i) {
|
||||
std::ostringstream oss;
|
||||
oss << "\t.short\t" << i << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::string space(int s) {
|
||||
std::ostringstream oss;
|
||||
oss << "\t.space\t" << s << "\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::string asciz(const std::string &s) {
|
||||
std::string quote = "\"";
|
||||
if(s.size() && s[0] == '\"')
|
||||
quote = "";
|
||||
return std::string("\t.asciz\t") + quote + s + quote + "\n";
|
||||
}
|
||||
|
||||
std::string makeArchiveHeader() const {
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << section(".idata$2")
|
||||
<< global_sym(import_directory_entry)
|
||||
<< rva(original_first_thunk)
|
||||
<< longdata(0)
|
||||
<< longdata(0)
|
||||
<< rva(dll_name_text)
|
||||
<< section(".idata$4")
|
||||
<< global_sym(original_first_thunk)
|
||||
<< section(".idata$5")
|
||||
<< global_sym(first_thunk)
|
||||
<< section(".idata$6")
|
||||
<< global_sym(import_desc_table);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string makeArchiveFooter() const {
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << section(".idata$3")
|
||||
<< global_sym(import_directory_term)
|
||||
<< space(20)
|
||||
<< global_sym(original_first_thunk_term)
|
||||
<< longdata(0)
|
||||
<< global_sym(first_thunk_term)
|
||||
<< longdata(0)
|
||||
<< section(".idata$7")
|
||||
<< global_sym(dll_name_text)
|
||||
<< asciz(libname);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string makePerImportData( size_t i ) const {
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << section(".text")
|
||||
<< global_sym(common_name(i))
|
||||
<< "\taddi\t1,1,16\n"
|
||||
<< "\tmflr\t0\n"
|
||||
<< "\tstw\t0,0(1)\n"
|
||||
<< "\tlis\t0," << impsym(first_thunk,i) << "@ha\n"
|
||||
<< "\tori\t0,0," << impsym(first_thunk,i) << "@l\n"
|
||||
<< "\tlwz\t0," << (4 * i) << "(0)\n"
|
||||
<< "\tmtlr\t0\n"
|
||||
<< "\tblrl\n"
|
||||
<< "\tlwz\t0,0(1)\n"
|
||||
<< "\taddi\t1,1,-16\n"
|
||||
<< "\tblr\n"
|
||||
<< rva(impsym(original_first_thunk,i))
|
||||
<< rva(impsym(import_desc_table,i))
|
||||
<< rva(impsym(first_thunk,i))
|
||||
<< rva(import_directory_entry)
|
||||
<< rva(import_directory_term)
|
||||
<< section(".idata$4")
|
||||
<< symbol(impsym(original_first_thunk,i))
|
||||
<< rva(impsym(import_desc_table,i))
|
||||
<< section(".idata$5")
|
||||
<< symbol(impsym(import_desc_table,i))
|
||||
<< shortdata(exports[i].getOrdinal())
|
||||
<< asciz(exports[i].getName())
|
||||
<< section(".idata$6")
|
||||
<< symbol(impsym(first_thunk,i))
|
||||
<< rva(impsym(import_desc_table,i));
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
size_t numFunctions() const { return exports.size(); }
|
||||
|
||||
void setKillAt( bool kill ) { kill_at = kill; }
|
||||
|
||||
private:
|
||||
bool kill_at;
|
||||
std::string libname, libsym;
|
||||
std::string import_directory_entry, import_directory_term;
|
||||
std::string original_first_thunk, first_thunk, dll_name_text;
|
||||
std::string original_first_thunk_term, first_thunk_term;
|
||||
std::string import_desc_table;
|
||||
std::vector<Export> exports;
|
||||
std::map<std::string,size_t> export_byname;
|
||||
int ordbase, mode;
|
||||
};
|
||||
|
||||
std::string maketemp
|
||||
( const std::string &prefix,
|
||||
const std::string &suffix,
|
||||
const std::string &payload ) {
|
||||
std::string template_accum =
|
||||
std::string("/tmp/") + prefix + "XXXXXX";
|
||||
std::vector<char> storage, duplicate;
|
||||
|
||||
for( size_t i = 0; i < template_accum.size(); i++ )
|
||||
storage.push_back(template_accum[i]);
|
||||
storage.push_back(0);
|
||||
|
||||
duplicate = storage;
|
||||
std::string result;
|
||||
mktemp(&duplicate[0]);
|
||||
result = &duplicate[0];
|
||||
result += suffix;
|
||||
std::ofstream of;
|
||||
of.open(result.c_str());
|
||||
of << payload;
|
||||
of.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _check( const std::string &varname, const std::string &varval ) {
|
||||
if( !varval.size() ) {
|
||||
fprintf( stderr, "You must specify %s\n", varname.c_str() );
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define check(x) _check(#x,x)
|
||||
|
||||
bool parseArg(int argc, char **argv,
|
||||
std::string &argval,
|
||||
const char *short_n,
|
||||
const char *long_n = 0,
|
||||
const char *extra_n = 0)
|
||||
{
|
||||
std::string argv_i;
|
||||
bool got_val = false;
|
||||
|
||||
if( !long_n ) { long_n = short_n; }
|
||||
|
||||
for( int i = 0; i < argc; i++ ) {
|
||||
argv_i = argv[i];
|
||||
if( argv_i == short_n ) {
|
||||
if( i < argc-1 )
|
||||
argval = argv[i+1];
|
||||
got_val = true;
|
||||
} else if( argv_i == long_n ) {
|
||||
if( i < argc-1 )
|
||||
argval = argv[i+1];
|
||||
got_val = true;
|
||||
} else if( argv_i.substr(0,strlen(long_n)+1) ==
|
||||
std::string(long_n) + "=" ) {
|
||||
argval = argv_i.substr(strlen(long_n)+1);
|
||||
got_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( !got_val && extra_n )
|
||||
return parseArg(argc,argv,argval,short_n,extra_n);
|
||||
|
||||
return got_val;
|
||||
}
|
||||
|
||||
int main( int argc, char **argv ) {
|
||||
int i, ln = 0;
|
||||
bool dont_delete = false, kill_at = false;
|
||||
DefFile f;
|
||||
std::string line;
|
||||
std::string def_file, exp_file, dll_name, imp_name,
|
||||
as_name = "as", ar_name = "ar cq", foo;
|
||||
std::ifstream in_def;
|
||||
|
||||
parseArg(argc,argv,def_file,"-d","--def","--input-def");
|
||||
parseArg(argc,argv,exp_file,"-e","--exp","--output-exp");
|
||||
parseArg(argc,argv,imp_name,"-l","--lib","--output-lib");
|
||||
parseArg(argc,argv,dll_name,"-D","--dll","--dll-name");
|
||||
parseArg(argc,argv,as_name, "-S","--as");
|
||||
dont_delete = parseArg(argc,argv,foo,"-n");
|
||||
kill_at = parseArg(argc,argv,foo,"-k");
|
||||
|
||||
check(def_file);
|
||||
check(imp_name);
|
||||
|
||||
int err = 0;
|
||||
std::string export_file, archive_header_file, archive_footer_file;
|
||||
std::vector<std::string> archive_member_files;
|
||||
std::ostringstream make_exp;
|
||||
std::ostringstream make_lib, archive_it;
|
||||
|
||||
f.setKillAt(kill_at);
|
||||
|
||||
in_def.open(def_file.c_str());
|
||||
while( std::getline(in_def,line) )
|
||||
if(!f.parse(++ln,line)) {
|
||||
fprintf( stderr, "Ungrammatic def file\n" );
|
||||
goto cleanup;
|
||||
}
|
||||
in_def.close();
|
||||
|
||||
unlink(imp_name.c_str());
|
||||
|
||||
export_file = maketemp("exports",".s",f.makeExportData());
|
||||
archive_header_file = maketemp("arch_hdr",".s",f.makeArchiveHeader());
|
||||
archive_footer_file = maketemp("arch_end",".s",f.makeArchiveFooter());
|
||||
for( size_t i = 0; i < f.numFunctions(); i++ )
|
||||
archive_member_files.push_back
|
||||
(maketemp("arch_mem",".s",f.makePerImportData(i)));
|
||||
|
||||
if( exp_file.size() ) {
|
||||
make_exp << as_name << " -mlittle -o " << exp_file << " " << export_file;
|
||||
|
||||
fprintf( stderr, "execute %s\n", make_exp.str().c_str() );
|
||||
|
||||
if( (err = system(make_exp.str().c_str())) ) {
|
||||
fprintf(stderr, "Failed to execute %s\n", make_exp.str().c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
make_lib << as_name << " -mlittle -o " << archive_header_file << ".o "
|
||||
<< archive_header_file
|
||||
<< " && "
|
||||
<< as_name << " -mlittle -o " << archive_footer_file << ".o "
|
||||
<< archive_footer_file;
|
||||
|
||||
archive_it << ar_name << " " << imp_name << " "
|
||||
<< archive_header_file << ".o";
|
||||
for( size_t i = 0; i < f.numFunctions(); i++ ) {
|
||||
make_lib << " && "
|
||||
<< as_name << " -mlittle -o "
|
||||
<< archive_member_files[i] << ".o "
|
||||
<< archive_member_files[i];
|
||||
archive_it << " " << archive_member_files[i] << ".o";
|
||||
}
|
||||
|
||||
archive_it << " " << archive_footer_file << ".o";
|
||||
make_lib << " && " << archive_it.str();
|
||||
|
||||
fprintf( stderr, "execute %s\n", make_lib.str().c_str() );
|
||||
|
||||
if( (err = system(make_lib.str().c_str())) ) {
|
||||
fprintf(stderr, "Failed to execute %s\n", make_exp.str().c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if( !dont_delete ) {
|
||||
unlink(export_file.c_str());
|
||||
unlink(archive_header_file.c_str());
|
||||
unlink((archive_header_file + ".o").c_str());
|
||||
unlink(archive_footer_file.c_str());
|
||||
unlink((archive_footer_file + ".o").c_str());
|
||||
for( size_t i = 0; i < f.numFunctions(); i++ ) {
|
||||
unlink(archive_member_files[i].c_str());
|
||||
unlink((archive_member_files[i] + ".o").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#ifndef FORK_EXECVP_H
|
||||
#define FORK_EXECVP_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ProcessHolder {
|
||||
public:
|
||||
virtual ~ProcessHolder() { }
|
||||
virtual std::string ReadStdError() = 0;
|
||||
virtual bool ProcessStarted() const = 0;
|
||||
virtual bool EndOfStream() const = 0;
|
||||
virtual int GetStatus() const = 0;
|
||||
};
|
||||
|
||||
class Process {
|
||||
public:
|
||||
Process( ProcessHolder *h ) : holder(h) { }
|
||||
~Process() { delete holder; }
|
||||
ProcessHolder *operator -> () const { return holder; }
|
||||
operator ProcessHolder *() const { return (holder && holder->ProcessStarted()) ? holder : NULL; }
|
||||
private:
|
||||
ProcessHolder *holder;
|
||||
};
|
||||
|
||||
ProcessHolder *fork_execvp( const std::vector<std::string> &args );
|
||||
|
||||
#endif//FORK_EXECVP_H
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include "fork_execvp.h"
|
||||
|
||||
class UnixProcessHolder : public ProcessHolder {
|
||||
public:
|
||||
UnixProcessHolder( const std::vector<std::string> &args )
|
||||
: read_fd(-1), child_pid(-1), at_end(false), status(-1) {
|
||||
std::vector<char *> argvect(args.size()+1);
|
||||
for( size_t i = 0; i < args.size(); i++ )
|
||||
argvect[i] = (char *)args[i].c_str();
|
||||
argvect[args.size()] = 0;
|
||||
|
||||
int err_fd[2];
|
||||
|
||||
if( pipe( err_fd ) == -1 )
|
||||
return;
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
child_pid = fork();
|
||||
|
||||
if( !child_pid ) {
|
||||
dup2( err_fd[1], 2 );
|
||||
close( err_fd[0] );
|
||||
close( err_fd[1] );
|
||||
|
||||
execvp(args[0].c_str(), &argvect[0] );
|
||||
exit(1);
|
||||
} else {
|
||||
close( err_fd[1] );
|
||||
read_fd = err_fd[0];
|
||||
}
|
||||
}
|
||||
|
||||
~UnixProcessHolder() {
|
||||
close( read_fd );
|
||||
}
|
||||
|
||||
std::string ReadStdError() {
|
||||
char buf[1024];
|
||||
int rl = read( read_fd, buf, sizeof(buf) );
|
||||
if( rl < 1 ) {
|
||||
at_end = true;
|
||||
waitpid( child_pid, &status, 0 );
|
||||
return "";
|
||||
}
|
||||
return std::string(buf, rl);
|
||||
}
|
||||
|
||||
bool ProcessStarted() const { return child_pid != -1; }
|
||||
bool EndOfStream() const { return at_end; }
|
||||
int GetStatus() const { return status; }
|
||||
|
||||
private:
|
||||
bool at_end;
|
||||
int read_fd;
|
||||
int child_pid;
|
||||
int status;
|
||||
};
|
||||
|
||||
ProcessHolder *fork_execvp( const std::vector<std::string> &args ) {
|
||||
return new UnixProcessHolder( args );
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
#include <windows.h>
|
||||
#include "fork_execvp.h"
|
||||
|
||||
class Win32ProcessHolder : public ProcessHolder {
|
||||
public:
|
||||
Win32ProcessHolder( const std::string &args )
|
||||
: ErrorRead(INVALID_HANDLE_VALUE),
|
||||
ProcessHandle(INVALID_HANDLE_VALUE),
|
||||
StreamEnded(false) {
|
||||
HANDLE ErrorWrite = NULL, ErrorReadTemp = NULL;
|
||||
PROCESS_INFORMATION pi = { };
|
||||
SECURITY_ATTRIBUTES sa = { };
|
||||
STARTUPINFO si = { };
|
||||
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
if(!CreatePipe(&ErrorReadTemp, &ErrorWrite, &sa, 0)) return;
|
||||
|
||||
if(!DuplicateHandle(GetCurrentProcess(), ErrorReadTemp,
|
||||
GetCurrentProcess(), &ErrorRead,
|
||||
0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS)) return;
|
||||
|
||||
CloseHandle(ErrorReadTemp);
|
||||
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
si.hStdError = ErrorWrite;
|
||||
|
||||
if(!CreateProcess(NULL, (char *)args.c_str(), NULL, NULL, TRUE,
|
||||
0, NULL, NULL, &si, &pi)) return;
|
||||
|
||||
ProcessHandle = pi.hProcess;
|
||||
|
||||
CloseHandle(ErrorWrite);
|
||||
}
|
||||
|
||||
~Win32ProcessHolder() {
|
||||
CloseHandle( ErrorRead );
|
||||
CloseHandle( ProcessHandle );
|
||||
}
|
||||
|
||||
std::string ReadStdError() {
|
||||
char Buf[1024];
|
||||
DWORD ReadBytes;
|
||||
|
||||
if( StreamEnded ) return "";
|
||||
|
||||
StreamEnded =
|
||||
!ReadFile(ErrorRead, Buf, sizeof(Buf), &ReadBytes, NULL) ||
|
||||
!ReadBytes;
|
||||
|
||||
if( !StreamEnded ) return std::string(Buf, ReadBytes);
|
||||
else return "";
|
||||
}
|
||||
|
||||
bool EndOfStream() const {
|
||||
return StreamEnded;
|
||||
}
|
||||
|
||||
bool ProcessStarted() const {
|
||||
return ProcessHandle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
int GetStatus() const {
|
||||
DWORD Status = 1;
|
||||
if( ProcessHandle != INVALID_HANDLE_VALUE ) {
|
||||
WaitForSingleObject( ProcessHandle, INFINITE );
|
||||
GetExitCodeProcess( ProcessHandle, &Status );
|
||||
}
|
||||
return Status;
|
||||
}
|
||||
|
||||
private:
|
||||
bool StreamEnded;
|
||||
HANDLE ErrorRead, ProcessHandle;
|
||||
};
|
||||
|
||||
std::string quote_escape( const std::string &_str ) {
|
||||
std::string str = _str;
|
||||
size_t q;
|
||||
|
||||
q = str.find('\"');
|
||||
while( q != std::string::npos ) {
|
||||
str.replace(q, 1, "\\\"");
|
||||
q = str.find('\"');
|
||||
}
|
||||
|
||||
return std::string("\"") + str + "\"";
|
||||
}
|
||||
|
||||
ProcessHolder *fork_execvp( const std::vector<std::string> &args ) {
|
||||
std::string argstring;
|
||||
ProcessHolder *holder;
|
||||
|
||||
for( std::vector<std::string>::const_iterator i = args.begin();
|
||||
i != args.end();
|
||||
i++ ) {
|
||||
if( i != args.begin() )
|
||||
argstring += " ";
|
||||
argstring += quote_escape(*i);
|
||||
}
|
||||
|
||||
return new Win32ProcessHolder( argstring );
|
||||
}
|
||||
Reference in New Issue
Block a user