Способ найти размер и местоположение дополнения в структуре?

Я пытаюсь записать инструмент, который возьмет в качестве входа некоторый код C, содержащий структуры. Это скомпилирует код, затем найти и произвести размер, и смещение любого дополнения компилятора решает добавить к структурам в нем. Это довольно просто, чтобы сделать вручную для известной структуры с помощью offsetof, sizeof, и некоторого дополнения, но я не могу выяснить простой способ сделать это автоматически для любой входной структуры.

Если я знал, как выполнить итерации через все элементы в структуре, я думаю, что мог записать инструмент без проблем, но насколько я знаю, что нет никакого способа сделать это. Я надеюсь, что некоторые люди StackOverflow будут знать путь. Однако я не застреваю в своем подходе, и я, конечно, открыт для любых альтернативных подходов к нахождению дополнения в структуре.

9
задан toolic 20 July 2010 в 23:19
поделиться

10 ответов

Разве это не то, чем занимается Пахоле ?

6
ответ дан 4 December 2019 в 11:03
поделиться

Я не верю, что существует какое-либо средство общего назначения для самоанализа / рефлексии в C. Это то, для чего предназначены Java или C #.

-1
ответ дан 4 December 2019 в 11:03
поделиться

Попросите ваш инструмент проанализировать определение структуры, чтобы найти имена полей, затем сгенерировать код C, который печатает описание заполнения структуры, и, наконец, скомпилировать и запустить этот код C. Пример кода Perl для второй части:

printf "const char *const field_names[] = {%s};\n",
       join(", ", map {"\"$_\""} @field_names);
printf "const size_t offsets[] = {%s, %s};\n",
       join(", ", map {"offsetof(struct $struct_name, $_)"} @field_names),
       "sizeof(struct $struct_name)";
print <<'EOF'
for (i = 0; i < sizeof(field_names)/sizeof(*field_names); i++) {
    size_t padding = offsets[i+1] - offsets[i];
    printf("After %s: %zu bytes of padding\n", field_names[i], padding);
}
EOF

C очень сложно разбирать, но вас интересует только очень небольшая часть языка, и похоже, что у вас есть некоторый контроль над исходными файлами, поэтому простой синтаксический анализатор должен сделать свое дело. Поиск CPAN обнаруживает Devel :: Tokenizer :: C и несколько C :: модулей в качестве кандидатов (я ничего о них не знаю, кроме их имен). Если вам действительно нужен точный синтаксический анализатор C, есть Cil , но вы должны написать свой анализ на Ocaml.

1
ответ дан 4 December 2019 в 11:03
поделиться

В языке C++ нет функции итерации по членам структуры, поэтому я думаю, что вам не повезло.

Возможно, вы сможете сократить часть кодовой таблицы с помощью макроса, но я думаю, что вы застряли на явном указании всех членов.

-1
ответ дан 4 December 2019 в 11:03
поделиться

Вы можете использовать Exuberant Ctags для анализа исходных файлов вместо использования модуля CPAN или взлома чего-либо самостоятельно. Например, для следующего кода:

typedef struct _foo {
    int a;
    int b;
} foo;

ctags выдает следующее:

_foo    x.c     /^typedef struct _foo {$/;"     s                               file:
a       x.c     /^    int a;$/;"                m       struct:_foo             file:
b       x.c     /^    int b;$/;"                m       struct:_foo             file:
foo     x.c     /^} foo;$/;"                    t       typeref:struct:_foo     file:

Первого, четвертого и пятого столбцов должно быть достаточно, чтобы вы могли определить, какие типы структур существуют и каковы их члены. Вы можете использовать эту информацию для создания программы C, которая определяет, сколько заполнения имеет каждый тип структуры.

2
ответ дан 4 December 2019 в 11:03
поделиться

Вы можете попробовать pstruct .

Я никогда не использовал это, но я искал способ, которым вы могли бы использовать удары, и это звучит так, как будто это отвечало всем требованиям.

Если это не так, я бы посоветовал поискать другие способы разобрать информацию о ударах.

2
ответ дан 4 December 2019 в 11:03
поделиться

Я предпочитаю читать и писать в буфер, а затем использовать функцию, загружающую элементы структуры из буфера . Это более переносимо, чем чтение непосредственно в структуру или использование memcpy . Кроме того, этот алгоритм избавляет от беспокойства о заполнении компилятора и также может быть настроен для обработки Endianess.

Правильная и надежная программа стоит больше, чем время, потраченное на сжатие двоичных данных.

3
ответ дан 4 December 2019 в 11:03
поделиться

Допустим, у вас есть следующий module.h:

typedef void (*handler)(void);

struct foo {
  char a;
  double b;
  int c;
};

struct bar {
  float y;
  short z;
};

Программа Perl для генерации распаковки шаблонов начинается с обычного front matter:

#! /usr/bin/perl

use warnings;
use strict;

sub usage { "Usage: $0 header\n" }

С structs мы подаем заголовок на ctags и из его вывода собираем члены struct. В результате получается хэш, ключами которого являются имена структур, а значениями - массивы пар вида [$member_name, $type].

Обратите внимание, что он обрабатывает только несколько типов 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;
}

Предполагая, что заголовок может быть включен сам по себе, generate_source генерирует программу на Си, которая выводит смещения на стандартный вывод, заполняет структуры фиктивными значениями и записывает необработанные структуры на стандартный вывод, предваряя их соответствующими размерами в байтах.

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;
}

Создайте шаблон для unpack, где параметром $members является значение в хэше, возвращаемом structs, дополненное смещениями (i.e. arrayrefs вида [$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;
}

Наконец, мы добрались до основной программы, где первой задачей является генерация и компиляция программы на языке Си:

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";

Теперь читаем вывод сгенерированной программы и декодируем структуры:

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: $!";
  }
}

Вывод:

$ ./unpack module.h 
bar: 0 1
foo: 2 3 4

Для справки, программа на языке Си, сгенерированная для модуля. his

#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;
}
4
ответ дан 4 December 2019 в 11:03
поделиться

Взломать Convert::Binary::C

2
ответ дан 4 December 2019 в 11:03
поделиться

Если у вас есть доступ к Visual C ++, вы можете добавить следующую директиву, чтобы компилятор сообщал, где и сколько было добавлено отступов:

#pragma warning(enable : 4820) 

В этот момент вы, вероятно, можете просто использовать вывод cl.exe и перейти вечеринка.

1
ответ дан 4 December 2019 в 11:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: