simply take
a reference to a Perl variable. (Although that value certainly is a
memory address, it's not the address where the variable's contents are
stored.)
Template code C promises to pack a "pointer to a fixed length string".
Isn't this what we want? Let's try:
# allocate some storage and pack a pointer to it
my $memory = "\x00" x $size;
my $memptr = pack( 'P', $memory );
But wait: doesn't C just return a sequence of bytes? How can we pass this
string of bytes to some C code expecting a pointer which is, after all,
nothing but a number? The answer is simple: We have to obtain the numeric
address from the bytes returned by C.
my $ptr = unpack( 'L!', $memptr );
Obviously this assumes that it is possible to typecast a pointer
to an unsigned long and vice versa, which frequently works but should not
be taken as a universal law. - Now that we have this pointer the next question
is: How can we put it to good use? We need a call to some C function
where a pointer is expected. The read(2) system call comes to mind:
ssize_t read(int fd, void *buf, size_t count);
After reading L explaining how to use C we can write
this Perl function copying a file to standard output:
require 'syscall.ph'; # run h2ph to generate this file
sub cat($){
my $path = shift();
my $size = -s $path;
my $memory = "\x00" x $size; # allocate some memory
my $ptr = unpack( 'L', pack( 'P', $memory ) );
open( F, $path ) || die( "$path: cannot open ($!)\n" );
my $fd = fileno(F);
my $res = syscall( &SYS_read, fileno(F), $ptr, $size );
print $memory;
close( F );
}
This is neither a specimen of simplicity nor a paragon of portability but
it illustrates the point: We are able to sneak behind the scenes and
access Perl's otherwise well-guarded memory! (Important note: Perl's
C does I require you to construct pointers in this roundabout
way. You simply pass a string variable, and Perl forwards the address.)
How does C with C work? Imagine some pointer in the buffer
about to be unpacked: If it isn't the null pointer (which will smartly
produce the C value) we have a start address - but then what?
Perl has no way of knowing how long this "fixed length string" is, so
it's up to you to specify the actual size as an explicit length after C.
my $mem = "abcdefghijklmn";
print unpack( 'P5', pack( 'P', $mem ) ); # prints "abcde"
As a consequence, C ignores any number or C<*> after C.
Now that we have seen C
at work, we might as well give C
a whirl.
Why do we need a second template code for packing pointers at all? The
answer lies behind the simple fact that an C with C promises
a null-terminated string starting at the address taken from the buffer,
and that implies a length for the data item to be returned:
my $buf = pack( 'p', "abc\x00efhijklmn" );
print unpack( 'p', $buf ); # prints "abc"
Albeit this is apt to be confusing: As a consequence of the length being
implied by the string's length, a number after pack code C
is a repeat
count, not a length as after C
.
Using C with C or C
to get the address where C<$x> is
actually stored must be used with circumspection. Perl's internal machinery
considers the relation between a variable and that address as its very own
private matter and doesn't really care that we have obtained a copy. Therefore:
=over 4
=item *
Do not use C with C or C
to obtain the address of variable
that's bound to go out of scope (and thereby freeing its memory) before you
are done with using the memory at that address.
=item *
Be very careful with Perl operations that change the value of the
variable. Appending something to the variable, for instance, might require
reallocation of its storage, leaving you with a pointer into no-man's land.
=item *
Don't think that you can get the address of a Perl variable
when it is stored as an integer or double number! C will
force the variable's internal representation to string, just as if you
had written something like C<$x .= ''>.
=back
It's safe, however, to P- or p-pack a string literal, because Perl simply
allocates an anonymous variable.
=head1 Pack Recipes
Here are a collection of (possibly) useful canned recipes for C
and C:
# Convert IP address for socket functions
pack( "C4", split /\./, "123.4.5.6" );
# Count the bits in a chunk of memory (e.g. a select vector)
unpack( '%32b*', $mask );
# Determine the endianness of your system
$is_little_endian = unpack( 'c', pack( 's', 1 ) );
$is_big_endian = unpack( 'xc', pack( 's', 1 ) );
# Determine the number of bits in a native integer
$bits = unpack( '%32I!', ~0 );
# Prepare argument for the nanosleep system call
my $timespec = pack( 'L!L!', $secs, $nanosecs );
For a simple memory dump we unpack some bytes into just as
many pairs of hex digits, and use C