diotalevi has asked for the wisdom of the Perl Monks concerning the following question:

I have a B::*OP object which is a pointer to an OP object in a specific location in memory ($$op). I would like to copy the OP object to some other location, I don't really care where, and then install something else into the location that I just copied the OP out of. Other things that already have a pointer to this op would now have a pointer to the new thing which might itself point to the old thing.

Not all OP objects are the same size so do I have to be concerned that a 28 byte hole would have to accomodate a 56 byte op? Is there slack space built in?

Replies are listed 'Best First'.
Re: Moving an OP and replacing it
by tachyon (Chancellor) on Jun 01, 2004 at 02:56 UTC

    Not all OP objects are the same size so do I have to be concerned that a 28 byte hole would have to accomodate a 56 byte op? Is there slack space built in?

    Put the question in C terms. If you wanted 28 bytes to store a C struct and had an array of such structs in memory would you:

    1. Expect that these structs will be represented in memory by a contiguous array of 28 byte chunks
    2. Expect that just in case someone wanted to hand insert a 56 byte struct the programmer/compiler allocated 28 bytes + 28 spare??????

    Now I don't know the real answer but I would be a little suprised if writing 56 bytes into a 28 byte hole did not overwrite the neighbouring 28 byte struct. Why have 28 and 56 byte structs if you always allocate 56 (which is what you are asking for)?

    The only way I could see it working is if you replace the old object with a new object (less than or equal in size to the old) that points to a freshly allocated OP object in a free chunk of memory.

    cheers

    tachyon

      I would be surprised as well but I don't know if the memory for the optree is actually packed that way or if it really could be treated that way.

        If you have a look at memory allocation and the op tree with -MO=Debug then you can see that Perl appears to be allocating space in line with requirements. You can see ops 60 32 40 32 40 124 40 76 bytes appart in physical memory.... Pot luck not overwriting a neighbouring op I would have thought:

        C:\>type test.pl my $greet = "Hello World!\n"; print $greet; C:\>perl -MO=Debug test.pl LISTOP (0x1bb3c00) op_next 0x0 op_sibling 0x0 op_ppaddr PL_ppaddr[OP_LEAVE] op_targ 1 op_type 178 op_seq 6616 op_flags 13 op_private 64 op_first 0x1bb3b1c op_last 0x1bb3bb8 op_children 5 OP (0x1bb3b1c) op_next 0x1bb3c28 op_sibling 0x1bb3c28 op_ppaddr PL_ppaddr[OP_ENTER] op_targ 0 op_type 177 op_seq 6607 op_flags 0 op_private 0 COP (0x1bb3c28) op_next 0x1bb3ccc op_sibling 0x1bb3ca4 op_ppaddr PL_ppaddr[OP_NEXTSTATE] op_targ 0 op_type 174 op_seq 6608 op_flags 1 op_private 0 cop_label cop_stashpv main cop_file test.pl cop_seq 6608 cop_arybase 0 cop_line 1 cop_warnings 0x0 BINOP (0x1bb3ca4) op_next 0x1bb3b5c op_sibling 0x1bb3b5c op_ppaddr PL_ppaddr[OP_SASSIGN] op_targ 0 op_type 36 op_seq 6611 op_flags 69 op_private 2 op_first 0x1bb3ccc op_last 0x1bb3d18 SVOP (0x1bb3ccc) op_next 0x1bb3d18 op_sibling 0x1bb3d18 op_ppaddr PL_ppaddr[OP_CONST] op_targ 3 op_type 5 op_seq 6609 op_flags 2 op_private 0 op_sv 0x0 Nullsv OP (0x1bb3d18) op_next 0x1bb3ca4 op_sibling 0x0 op_ppaddr PL_ppaddr[OP_PADSV] op_targ 1 op_type 9 op_seq 6610 op_flags 178 op_private 128 COP (0x1bb3b5c) op_next 0x1bb3b98 op_sibling 0x1bb3bb8 op_ppaddr PL_ppaddr[OP_NEXTSTATE] op_targ 0 op_type 174 op_seq 6612 op_flags 1 op_private 0 cop_label cop_stashpv main cop_file test.pl cop_seq 6612 cop_arybase 0 cop_line 2 cop_warnings 0x0 LISTOP (0x1bb3bb8) op_next 0x1bb3c00 op_sibling 0x0 op_ppaddr PL_ppaddr[OP_PRINT] op_targ 0 op_type 209 op_seq 6615 op_flags 5 op_private 0 op_first 0x1bb3b98 op_last 0x1bb3be0 op_children 2 OP (0x1bb3b98) op_next 0x1bb3be0 op_sibling 0x1bb3be0 op_ppaddr PL_ppaddr[OP_PUSHMARK] op_targ 0 op_type 3 op_seq 6613 op_flags 2 op_private 0 OP (0x1bb3be0) op_next 0x1bb3bb8 op_sibling 0x0 op_ppaddr PL_ppaddr[OP_PADSV] op_targ 1 op_type 9 op_seq 6614 op_flags 3 op_private 0 test.pl syntax OK C:\>perl -MO=Debug test.pl >debug test.pl syntax OK C:\>perl -ne "m/op_next\s*(\S+)/ and $h{$1}++;END{print \"$_\n\" for s +ort{$a<=>$b}map{hex}keys %h}" debug 0 29047644 29047704 29047736 29047776 29047808 29047848 29047972 29048012 29048088 C:\>perl -ne "m/op_next\s*(\S+)/ and $h{$1}++;END{print \"$_\n\" for s +ort{$a<=>$b}map{hex}keys %h}" debug >memloc C:\>perl -ne "print $_-$a,$/;$a=$_" memloc 0 29047644 60 32 40 32 40 124 40 76 C:\>

        cheers

        tachyon

Re: Moving an OP and replacing it
by TomDLux (Vicar) on Jun 01, 2004 at 02:07 UTC

    In C, it is sometimes appropriate to fiddle with the allocation of objects. In perl, the natural paradigm is to allow garbage collection to eliminate obsolete data, and new objects to be allocated where they will.

    Why do you want to allocate things in particular locations? Maybe you should create an object with Inline::C handling the details, if you REALLY need it ... but I bet you don't need it.

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

      I am attempting to do something s///-like on the optree which implies that either the object being replaced must either be altered (the move/replace I mentioned) or the pointers to it must be adjusted.
Re: Moving an OP and replacing it
by tilly (Archbishop) on Jun 01, 2004 at 07:33 UTC
    I really hope that this is for nothing more serious than an ACME::* module since I would hate for anyone to rely on code that tries to do something like this. It sounds extremely fragile.

    How are you going to recognize pointers to the thing that you're adjusting? Are you just going to do a substitution and pray that anything whose bytes are the address of that location really is a pointer to that location? Sure it is only 1 in 4 billion odds of being wrong, for each place you start a set of 4 bytes. Which means that if you did this on a process that was at 40 MB, you'd have 1% odds of randomly going wrong. And when you did go wrong, good luck debugging it!

      Oh that'd be silly - to just substr the process memory and just look for strings that were identical to the packed pointer. Here's I've half-copied, half-sketched the code that I'm running in http://lik.grenekatz.org/downloads/perl/Devel/UncheckedOps.pm.

      use vars qw( %ALLOPS %OPMAP @QUEUE_OPCODES_TO_FIX ); CHECK { walkallops_filtered( sub { foo( $op ) ... }, \ &queue_opcode ); fix_opcode( $_ ) for @QUEUE_OPCODES_TO_FIX; } sub queue_opcode { my $op = shift; push @QUEUE_OPCODES_TO_FIX, $op; } sub foo { my $op = shift; my $addr = $$op; $ALLOPS{ $addr } = $op; for my $m ( qw( next sibling ppaddr first last other pmreplroot pm +replstart pmnext ) ) { my $to = eval { ${ $op->$m } }; next unless $to; push @{$OPMAP{ $addr }}, "$to $m"; } } x_opcode { my $op = shift; my $addr = $$op; # Construct the new program fragment in reverse order so parent no +des # can point to child nodes. See the tree immedia # null # or # $OP # die # pushmark # null2 # gvsv my $gvsv = B::SVOP->new( gvsv => 2, '$!' ); my $null2 = B::OP->new( null => 6 ); $null2->private( 1 ); my $pushmark = B::OP->new( pushmark => 2 ); my $die = B::LISTOP->new( die => 5, $pushmark, $null2 ); $die->targ( 1 ); $die->private( 1 ); $pushmark->sibling( $null2 ); my $or = B::LOGOP->new( or => 2, $op, $die ); $or->private( 1 ); my $null = B::OP->new( null => 5 ); $null->private( 1 ); # Tree order my $orig_sibling = $op->sibling; $op->sibling( $die ); # Now thread the execution order. my $orig_next = $op->next; # EXT -> $op # -> OR # -> $orig_next || $pushmark # ( null2 -> gvsv ) # -> gvsv # -> die $op->next( $or ); $or->next( $orig_next ); $or->other( $pushmark ); $null->next( $orig_next ); $pushmark->next( $gvsv ); $null2->next( $gvsv ); # Also but this is skipped anyway. $gvsv->next( $die ); $die->next( $orig_next ); # Now change everything that currently points to $op so that it po +ints to my @referrents = @{ $OPMAP{ $addr } }; # use YAML 'Dump'; # print Dump( $addr, \%OPMAP ) or die $!;exit; foreach my $referrent ( @referrents ) { my ( $referrent_addr, $referrent_attr ) = split ' ', $referrent; print "$$op $referrent_addr $referrent_attr\n" or die $!; # my $co = B::class( $op ); # my $cn = $op->oldname; # my $c = B::class( $referrent_obj ); # my $n = $referrent_obj->oldname; # $referrent_obj->$referrent_attr( $op ); # print STDERR "$referrent_obj->$referrent_attr( $op )\n" or die $! +; } }