ASM: mixing assembler and C++
Posted 02-05-2012 at 12:05 PM by rainbowsally
Tags asm, assembler, computer mad science
This is for intel 32 bit assembler. For 64-bits the calling convention is a bit different. See previous entries (specifically asm 64 bits) if you need a leg-up to break the 64 bit barrier.
Features:
[If you really want to get down, you can use the mangled names as seen in an 'objdump -d' output but with some switches (mentioned below) you might find cutting corners using high level C is almost as much fun as hand coding this stuff in low down asm.]
Here's the main file written in C++ to demonstrate and test the calls to and from C++ and asm which is written in a file (not inlined, it's written in a separate *.s file).
Note the use of 'extern "C"' declarations to avoid having to use the C++ mangled names. These 'wrapper' functions may be annoying to asm programmers but by putting them in separate files and compiling with --omit-frame-pointers flag, can generate simple callable single-opcode jumps.
Here's the code to parse an input we get from a C++ class and either exit the loop or call the C++ again to execute a command through the shell.
About the demo itself: When it runs you can't 'chdir' or assign variables in the environment in this version, because we only 'interpret' one command locally and that is the 'exit' command.
The calls to C++ through the C intermediaries are marked in the sources below.
If you want to create your makefile with our makefile-creator temporarily name asm.s asm.cpp in order to allow the makefile-creator to generate compile/link rules.
Then type makefile-creator c++ asm2cxx to generate a C++ output file named asm2cxx.
Finally change the name of the file back to asm.s and do a global find/replace to change asm.cpp to asm.s several places in the Makefile and you're good to go.
If you want a tool that does this automatically, you might be interested in AutoBake (updated for use by "normals" at last). ;-)
http://rainbowsally.net/tkf/ftl/Auto...2-02-04.tar.gz
If you'd like a much simpler utility we made in this blog, you can try building this from sources here at the blog.
http://www.linuxquestions.org/questi...-part-2-34421/
Features:
- Asm functions that can call class functions (using an object pointer) through jump vectors written in C.
- Usage of extern "C" declarations to un-mangle names we need to keep simple for asm coding.
[If you really want to get down, you can use the mangled names as seen in an 'objdump -d' output but with some switches (mentioned below) you might find cutting corners using high level C is almost as much fun as hand coding this stuff in low down asm.]
Here's the main file written in C++ to demonstrate and test the calls to and from C++ and asm which is written in a file (not inlined, it's written in a separate *.s file).
Code:
// main.cpp -> asm2cxx
// Interfacing asm with C++ example
#include <stdio.h> // printf()
#include <string.h> // str*()
#include <malloc.h> // malloc(), free()
#include <stdlib.h> // system()
void dbg(){}
class Interp
{
private:
char* parsebuf;
const char* command;
public:
Interp();
~Interp();
char* get_parsebuf() { return parsebuf; }
void read_command();
void do_command();
};
// constructor/destructor
Interp::Interp() { parsebuf = (char*) malloc(4096); }
Interp::~Interp() { if(parsebuf) free(parsebuf); parsebuf = 0; }
// some class functions we can call from asm through jumps
// defined in C.
void Interp::read_command() { fgets(parsebuf, 4096, stdin); }
// does a system call, this will NOT do chdir though. That
// has to be handled by a direct call to chdir().
void Interp::do_command() {
if(!system(parsebuf))
printf(" [OK]\n");
else
printf(" [???]\n");
}
extern "C"
{
// Takes an object pointer on the stack,
// returns 0 to exit, else loop
void code_section();
int asm2cxx(Interp* o);
void print_prompt();
} // extern "C"
int main(int argc, char** argv)
{
dbg();
Interp* o = new Interp();
while(1)
{
int more;
print_prompt();
more = asm2cxx(o);
if(! more)
{
printf("Exiting asm2cxx demo\n");
break;
}
}
return 0;
}
static char exit_str[] = {"exit"};
extern "C"
{
void c2cxx_read_command(Interp* o) { o->read_command(); }
void c2cxx_do_command(Interp* o) { o->do_command(); }
const char* c2cxx_get_parsebuf(Interp* o) { return o->get_parsebuf(); }
}
Here's the code to parse an input we get from a C++ class and either exit the loop or call the C++ again to execute a command through the shell.
About the demo itself: When it runs you can't 'chdir' or assign variables in the environment in this version, because we only 'interpret' one command locally and that is the 'exit' command.
The calls to C++ through the C intermediaries are marked in the sources below.
Code:
// asm.s - 32 bit intel assembler, ATT syntax
/* notes:
*
* C and C++ style comments are OK anywhere except following
* a mnemonic unless the mnemonic is terminated with a semicolon.
* Hashmark '#' comments ala sh comments can be put anywhere.
*
* As in C declarations can be separate from definitions for
* .data and .text (code). And no external lookups are done
* until link-time, so including headers is not necessary.
*/
//////////////////////////////////////
// data section
.data
.align 4;
promptstr:
.string "\nASM2CXX: "
exitstr:
.string "exit"
//////////////////////////////////////
// code declarations
.text;
.align 4;
// void print_prompt();
.globl print_prompt
.type print_prompt, @function
// int asm2cxx(Interp* o);
.globl asm2cxx
.type asm2cxx, @function
//////////////////////////////////////
// code definitions
print_prompt:
push $promptstr;
call printf;
pop %eax; // clean up stack
ret;
asm2cxx:
// grab object ptr and save regs
mov 4(%esp), %eax;
pushl %ebx; pushl %edi; pushl %esi;
// ebx = o
movl %eax, %ebx
// reserve enough stack area for up to 4 call params en bloc
subl $16, %esp
// get the command pointer (ebx = obj ptr)
// set param[0] = obj ptr
movl %ebx, (%esp)
call c2cxx_read_command ### call C++ through C
// eax = command string
// skip whitespaces
cmpb $32, (%eax)
jg .L_end_skip_leading
.L_begin_skip_leading:
addl $1, %eax
cmpb $32, (%eax)
jle .L_begin_skip_leading
.L_end_skip_leading:
// if command = "exit"
movl $exitstr, %edi; movl $4, %ecx; movl %eax, %esi; repz cmpsb
jne .L_not_exit
movzbl 4(%eax), %edx
leal 4(%eax), %ecx; // step to end of "exit" if match
testb %dl, %dl
je .L_exit
// and trailing whitespaces ok (ecx is our str ptr)
jmp .L_chk_ws
.L_next_ws:
addl $1, %ecx
movzbl (%ecx), %edx
testb %dl, %dl
je .L_exit
.L_chk_ws:
cmpb $32, %dl
jle .L_next_ws
.L_exit:
// return FALSE (don't loop again) if end of input
xorl %eax, %eax
testb %dl, %dl
je .L_clean_stack
// do the command and return TRUE (loop again)
.L_not_exit:
// set param[0] = obj ptr
movl %ebx, (%esp)
call c2cxx_do_command ### call C++ through C
// return TRUE
movl $1, %eax
.L_clean_stack:
addl $16, %esp
popl %esi; popl %edi; popl %ebx
ret
Then type makefile-creator c++ asm2cxx to generate a C++ output file named asm2cxx.
Finally change the name of the file back to asm.s and do a global find/replace to change asm.cpp to asm.s several places in the Makefile and you're good to go.
If you want a tool that does this automatically, you might be interested in AutoBake (updated for use by "normals" at last). ;-)
http://rainbowsally.net/tkf/ftl/Auto...2-02-04.tar.gz
If you'd like a much simpler utility we made in this blog, you can try building this from sources here at the blog.
http://www.linuxquestions.org/questi...-part-2-34421/
Total Comments 0




