Diciamo che avete il seguente module.h
:
programma
typedef void (*handler)(void);
struct foo {
char a;
double b;
int c;
};
struct bar {
float y;
short z;
};
Un Perl per generare unpack
modelli inizia con la materia anteriore consueto:
#! /usr/bin/perl
use warnings;
use strict;
sub usage { "Usage: $0 header\n" }
Con structs
, alimentiamo l'intestazione a ctags
e da il suo output raccoglie membri struct. Il risultato è un hash le cui chiavi sono nomi di strutture e i cui valori sono array di coppie del modulo [$member_name, $type]
.
Nota che gestisce solo alcuni tipi di C.
sub structs {
my($header) = @_;
open my $fh, "-|", "ctags", "-f", "-", $header
or die "$0: could not start ctags";
my %struct;
while (<$fh>) {
chomp;
my @f = split /\t/;
next unless @f >= 5 &&
$f[3] eq "m" &&
$f[4] =~ /^struct:(.+)/;
my $struct = $1;
die "$0: unknown type in $f[2]"
unless $f[2] =~ m!/\^\s*(float|char|int|double|short)\b!;
# [ member-name => type ]
push @{ $struct{$struct} } => [ $f[0] => $1 ];
}
wantarray ? %struct : \%struct;
}
Supponendo che l'intestazione può essere incluso per sé, generate_source
genera un programma C che stampa offset per l'uscita standard, riempie le strutture con valori fittizi, e scrive le strutture prime per l'uscita standard preceduto da rispettivi formati in byte.
sub generate_source {
my($struct,$header) = @_;
my $path = "/tmp/my-offsets.c";
open my $fh, ">", $path
or die "$0: open $path: $!";
print $fh <<EOStart;
#include <stdio.h>
#include <stddef.h>
#include <$header>
void print_buf(void *b, size_t n) {
char *c = (char *) b;
printf("%zd\\n", n);
while (n--) {
fputc(*c++, stdout);
}
}
int main(void) {
EOStart
my $id = "a1";
my %id;
foreach my $s (sort keys %$struct) {
$id{$s} = $id++;
print $fh "struct $s $id{$s};\n";
}
my $value = 0;
foreach my $s (sort keys %$struct) {
for (@{ $struct->{$s} }) {
print $fh <<EOLine;
printf("%lu\\n", offsetof(struct $s,$_->[0]));
$id{$s}.$_->[0] = $value;
EOLine
++$value;
}
}
print $fh qq{printf("----\\n");\n};
foreach my $s (sort keys %$struct) {
print $fh "print_buf(&$id{$s}, sizeof($id{$s}));\n";
}
print $fh <<EOEnd;
return 0;
}
EOEnd
close $fh or warn "$0: close $path: $!";
$path;
}
generare un modello per unpack
in cui il parametro $members
è un valore nel hash restituito da structs
che è stato convertito con offset (cioè, arrayrefs della forma [$member_name, $type, $offset]
:
sub template {
my($members) = @_;
my %type2tmpl = (
char => "c",
double => "d",
float => "f",
int => "i!",
short => "s!",
);
join " " =>
map '@![' . $_->[2] . ']' . $type2tmpl{ $_->[1] } =>
@$members;
}
Infine , raggiungiamo il programma principale in cui il primo compito è generare e compilare il programma C:
die usage unless @ARGV == 1;
my $header = shift;
my $struct = structs $header;
my $src = generate_source $struct, $header;
(my $cmd = $src) =~ s/\.c$//;
system("gcc -I`pwd` -o $cmd $src") == 0
or die "$0: gcc failed";
Ora leggiamo l'output del programma generato e decodificare lo struct:
my @todo = map @{ $struct->{$_} } => sort keys %$struct;
open my $fh, "-|", $cmd
or die "$0: start $cmd failed: $!";
while (<$fh>) {
last if /^-+$/;
chomp;
my $m = shift @todo;
push @$m => $_;
}
if (@todo) {
die "$0: unfilled:\n" .
join "" => map " - $_->[0]\n", @todo;
}
foreach my $s (sort keys %$struct) {
chomp(my $length = <$fh> || die "$0: unexpected end of input");
my $bytes = read $fh, my($buf), $length;
if (defined $bytes) {
die "$0: unexpected end of input" unless $bytes;
print "$s: @{[unpack template($struct->{$s}), $buf]}\n";
}
else {
die "$0: read: $!";
}
}
uscita:
$ ./unpack module.h
bar: 0 1
foo: 2 3 4
Per riferimento, il programma C generato per module.h
è
#include <stdio.h>
#include <stddef.h>
#include <module.h>
void print_buf(void *b, size_t n) {
char *c = (char *) b;
printf("%zd\n", n);
while (n--) {
fputc(*c++, stdout);
}
}
int main(void) {
struct bar a1;
struct foo a2;
printf("%lu\n", offsetof(struct bar,y));
a1.y = 0;
printf("%lu\n", offsetof(struct bar,z));
a1.z = 1;
printf("%lu\n", offsetof(struct foo,a));
a2.a = 2;
printf("%lu\n", offsetof(struct foo,b));
a2.b = 3;
printf("%lu\n", offsetof(struct foo,c));
a2.c = 4;
printf("----\n");
print_buf(&a1, sizeof(a1));
print_buf(&a2, sizeof(a2));
return 0;
}
I don' t capire cosa stai chiedendo. Vuoi costruire un sistema di riflessione generico per il linguaggio C? La riflessione in C è un po 'come voler attraversare l'Oceano Atlantico con una moto ... –
Scusa se non sono stato chiaro nella mia domanda. Non sto cercando un sistema di riflessione generico (spero!). Ho pensato che la mia soluzione avrebbe comportato l'analisi del codice sorgente C con Perl e la generazione di qualche codice C modificato con una dimensione e una chiamata offset per ogni elemento nella struttura.Ciò fornirebbe la dimensione e la posizione di tutti gli elementi nella struct, e da quello è banale trovare e riportare qualsiasi padding che la struct contenga. Sembra un approccio ragionevole, o andresti sul problema in un altro modo? –