LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices



Rate this Entry

ASM: mixing assembler and C++

Posted 02-05-2012 at 01:05 PM by rainbowsally
Updated 08-06-2014 at 11:30 AM by rainbowsally

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:
  • 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(); }
}
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.
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
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.org/rainbowsally...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/
Posted in Uncategorized
Views 898 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 04:35 AM.

Main Menu
Advertisement

Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration