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


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.

  • 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).

// 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
    char* parsebuf;
    const char* command;
    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() { 
    printf(" [OK]\n");
    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)
  Interp* o = new Interp();
    int more;
    more = asm2cxx(o);
    if(! more)
      printf("Exiting asm2cxx demo\n");
  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.
// 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
  .align 4;

  .string "\nASM2CXX: "
  .string "exit"

// code declarations

  .align 4;
// void print_prompt();
.globl print_prompt
.type print_prompt, @function

// int asm2cxx(Interp* o);
.globl asm2cxx
.type asm2cxx, @function

// code definitions

  push  $promptstr;
  call  printf;
  pop  %eax;          // clean up stack

  // 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
  addl  $1, %eax
  cmpb  $32, (%eax)
  jle .L_begin_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
  addl  $1, %ecx
  movzbl  (%ecx), %edx
  testb %dl, %dl
  je  .L_exit
  cmpb  $32, %dl
  jle .L_next_ws

  // 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)
  // set param[0] = obj ptr
  movl  %ebx, (%esp)
  call  c2cxx_do_command  ### call C++ through C

  // return TRUE
  movl  $1, %eax

  addl  $16, %esp
  popl  %esi;  popl  %edi;  popl  %ebx  
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). ;-)

If you'd like a much simpler utility we made in this blog, you can try building this from sources here at the blog.
Posted in Uncategorized
Views 986 Comments 0
« Prev     Main     Next »
Total Comments 0




All times are GMT -5. The time now is 06:19 PM.

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