Read PHP’s opcode

Vulcan Logic Disassembler

Vulcan Logic Disassembler is an extension which show how zend engine converts your script in opcode.
http://pecl.php.net/package/vld
I will explain how to install, use and read opcode.


First of all, you have to install PEAR:

aptitude install php-pear

Now you can add VLD:

pecl install "channel://pecl.php.net/vld-0.12.0"

Enable this extension by editing php.ini
Beware, this module is for cli, so it’s not the same php.ini.

nano /etc/php5/cli/php.ini

Add this line in the Dynamic Extensions zone.

extension=vld.so

How to use it?

php -dvld.dump_paths=1 -dvld.verbosity=0 -dvld.save_paths=1 -dvld.active=1 yourscript.php

Here parameters’ list with default vaules:

# Activate extension: boolean 
vld.active	 0
# The caracter used for writing columns: carater
vld.col_sep	 "\t"
# Dumps branch and paths: boolean
vld.dump_paths	 1
# Execute code in the same time: boolean
vld.execute	 1
# Format output analyze: boolean
vld.format	 0
# Where to save the file for graphviz .dot file: string
vld.save_dir	    /tmp
# dump path information has a graphviz .dot file: boolean
vld.save_paths	 0
# suppresses output and loading of auto_append_file respectively if vld.active=1 and vld.execute=0: boolean
vld.skip_append	 0
# suppresses output and loading of auto_prepend_file respectively if vld.active=1 and vld.execute=0: boolean
vld.skip_prepend 0
# Verbosities' level (0:none, 1, 2, 3): integer
vld.verbosity	 1

Now we can see the result from this source (http://blog.pascal-martin.fr/post/php-obtenir-dump-opcodes):
Php Code

< ?php
for ($i=0 ; $i>10 ; $i++) {
    if ($i % 2 === 0) {
        echo "Pair : ";
    }
    else {
        echo "Impair : ";
    }
    echo $i, "\n";
}

VLD Output

$ ~/bin/php-5.3/bin/php -dextension=vld.so -dvld.active=1 -dvld.verbosity=0 -dvld.execute=0 exemples/003-control.php 
filename:       /.../exemples/003-control.php
function name:  (null)
number of ops:  16
compiled vars:  !0 = $i
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   2     0  >   ASSIGN                                                   !0, 0
         1  >   IS_SMALLER                                       ~1      !0, 10
         2    > JMPZNZ                                        6          ~1, ->15
         3  >   POST_INC                                         ~2      !0
         4      FREE                                                     ~2
         5    > JMP                                                      ->1
   3     6  >   MOD                                              ~3      !0, 2
         7      IS_IDENTICAL                                     ~4      ~3, 0
         8    > JMPZ                                                     ~4, ->11
   4     9  >   ECHO                                                     'Pair+%3A+'
   5    10    > JMP                                                      ->12
   7    11  >   ECHO                                                     'Impair+%3A+'
   9    12  >   ECHO                                                     !0
        13      ECHO                                                     '%0A'
  10    14    > JMP                                                      ->3
  11    15  > > RETURN                                                   1
 
branch: #  0; line:     2-    2; sop:     0; eop:     0; out1:   1
branch: #  1; line:     2-    2; sop:     1; eop:     2; out1:  15; out2:   6
branch: #  3; line:     2-    2; sop:     3; eop:     5; out1:   1
branch: #  6; line:     3-    3; sop:     6; eop:     8; out1:   9; out2:  11
branch: #  9; line:     4-    5; sop:     9; eop:    10; out1:  12
branch: # 11; line:     7-    9; sop:    11; eop:    11; out1:  12
branch: # 12; line:     9-   10; sop:    12; eop:    14; out1:   3
branch: # 15; line:    11-   11; sop:    15; eop:    15
path #1: 0, 1, 15, 
path #2: 0, 1, 6, 9, 12, 3, 1, 15, 
path #3: 0, 1, 6, 11, 12, 3, 1, 15,

How to read it?

Explain this

Before the table we have 2 lines:

  • number of ops: how many operation with opcode will be done, here it’s sixteen,
  • compiled vars: the transcription of a php variable and his opcode version, here $i is reprensented by !0

Now the table:

  1. line: Integer, the line in php file
  2. #: Integer, opcode number
  3. *:?
  4. op: String, function use by zend engine
  5. fetch:?
  6. ext:?
  7. return: Mixed, op’s result which saved in a local variable
  8. operands: Mixed, arguments of op

Explain thoses lines (traduction from http://blog.pascal-martin.fr/post/php-obtenir-dump-opcodes)

  • op 0: We affect 0 to $i which represent by !0.
  • op 1: We test if $i is smaller than 10. The result is saved in ~1.
  • op 2: If ~1 is false, we jump to op 15. Else, we jump to op 6 after the for boucle in op 3,4,5.
  • op 3: Post-increment $i and save result in ~2 (third instruction in for boucle).
  • op 4: Delete ~2.
  • op 5: Jump to op 1 (end of the for boucle).
  • op 6: Compute modulo 2 on $i, and save it in ~3.
  • op 7: We test if ~3, is identical (operator ===) at 0. Save result in ~4.
  • op 8: If ~4 is false we jump to op 11.
  • op 9: Show “Pair : “.
  • op 10: Jump to op 12.
  • op 11: Show “Impair : “.
  • op 12: Show $i value.
  • op 13: Show the caracter end of line.
  • op 14: We jump to op 3.
  • op 15: End.

Graphical view

You can execute this command for having a picture of the path (only if you have setted vld.save_dir and vld.save_paths).
You have to install Graphiz for dot command:

aptitude install graphviz
dot -Tpng /tmp/paths.dot > /tmp/paths.png

More sources:

http://derickrethans.nl/more-source-analysis-with-vld.html (the graphical view of opcode came from here)
http://blog.ircmaxell.com/2012/07/the-anatomy-of-equals-opcode-analysis.html
http://blog.pascal-martin.fr/post/php-obtenir-dump-opcodes
http://blog.pascal-martin.fr/post/aller-plus-loin-avec-dump-opcodes-script-php

Descriptions of operators:

http://www.php.net/manual/en/internals2.opcodes.list.php