Digamos que tiene la siguiente module.h
:
programa
typedef void (*handler)(void);
struct foo {
char a;
double b;
int c;
};
struct bar {
float y;
short z;
};
Un Perl para generar unpack
plantillas comienza con el asunto delante habitual:
#! /usr/bin/perl
use warnings;
use strict;
sub usage { "Usage: $0 header\n" }
Con structs
, que alimentan la cabecera para ctags
y desde su salida recolecta miembros de la estructura. El resultado es un hash cuyas claves son nombres de estructuras y cuyos valores son matrices de pares del formulario [$member_name, $type]
.
Tenga en cuenta que maneja solo unos pocos tipos de 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;
}
Suponiendo que la cabecera se puede incluir por sí misma, generate_source
genera un programa C que imprime las compensaciones a la salida estándar, llena estructuras con valores ficticios, y escribe las estructuras primas a la salida estándar precedido por sus respectivos tamaños en bytes
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;
}
Generar una plantilla para unpack
donde el parámetro $members
es un valor en el hash devuelto por structs
que ha sido aumentada con offsets (es decir, arrayrefs de la 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;
}
Finalmente , llegamos al programa principal donde la primera tarea es generar y compilar el programa 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";
Ahora leemos la salida del programa generado y decodificar la estructuras:
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: $!";
}
}
Salida:
$ ./unpack module.h
bar: 0 1
foo: 2 3 4
Como referencia, el programa de C generado para module.h
es
#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' Entiendo lo que estás pidiendo. ¿Desea construir un sistema de reflexión genérico para el lenguaje C? Querer el reflejo en C es como querer cruzar el Océano Atlántico con una motocicleta ... –
Disculpe si no estaba claro en mi pregunta. No estoy buscando un sistema de reflexión genérico (¡espero!). Pensé que mi solución implicaría analizar el código fuente C con Perl y generar algún código C modificado con un tamaño y una llamada de compensación para cada elemento en la estructura.Eso proporcionaría el tamaño y la ubicación de todos los elementos en la estructura, y de ahí es trivial encontrar e informar cualquier relleno que contenga la estructura. ¿Parece un enfoque razonable o abordarías el problema de otra manera? –