2010-07-20 10 views
9

मैं एक ऐसा टूल लिखने की कोशिश कर रहा हूं जो इनपुट के रूप में कुछ सी कोड वाले स्ट्रक्चर के रूप में ले जाएगा। यह कोड संकलित करेगा, फिर संकलक को किसी भी पैडिंग के आकार और ऑफसेट को ढूंढें और आउटपुट करें, जिसमें संकलक इसके भीतर structs में जोड़ने का निर्णय लेता है। ऑफ़सेट, आकार, और कुछ अतिरिक्त उपयोग करके ज्ञात संरचना के लिए यह हाथ से करना बहुत सरल है, लेकिन मैं किसी भी इनपुट स्ट्रक्चर के लिए इसे स्वचालित रूप से करने का एक आसान तरीका नहीं समझ सकता।एक संरचना में पैडिंग के आकार और स्थान को खोजने का एक तरीका?

यदि मुझे पता था कि संरचना में सभी तत्वों के माध्यम से पुनरावृत्ति कैसे करें, तो मुझे लगता है कि मुझे कोई समस्या नहीं है, लेकिन जहां तक ​​मुझे पता है कि ऐसा करने का कोई तरीका नहीं है। मुझे उम्मीद है कि कुछ स्टैक ओवरफ्लो लोग एक रास्ता जान लेंगे। हालांकि, मैं अपने दृष्टिकोण में फंस नहीं गया हूं, और मैं संरचना में पैडिंग खोजने के लिए निश्चित रूप से किसी भी वैकल्पिक दृष्टिकोण के लिए खुला हूं।

+1

मैं डॉन ' आप समझ रहे हैं कि आप क्या पूछ रहे हैं। क्या आप सी भाषा के लिए एक सामान्य प्रतिबिंब प्रणाली बनाना चाहते हैं? सी में प्रतिबिंब चाहते हैं एक मोटरबाइक के साथ अटलांटिक महासागर पार करना चाहते हैं ... –

+0

क्षमा करें अगर मैं अपने प्रश्न में स्पष्ट नहीं था। मैं एक सामान्य प्रतिबिंब प्रणाली की तलाश नहीं कर रहा हूं (मुझे उम्मीद है!)। मुझे लगा कि मेरे समाधान में सीएल स्रोत कोड को पर्ल के साथ पार्स करना होगा और कुछ संशोधित सी कोड को संरचना में प्रत्येक तत्व के लिए आकार और ऑफसेट कॉल के साथ उत्पन्न करना होगा।इससे संरचना में सभी तत्वों का आकार और स्थान प्रदान किया जाएगा, और इससे संरचना में शामिल किसी भी पैडिंग को ढूंढना और रिपोर्ट करना मुश्किल है। क्या यह एक उचित दृष्टिकोण की तरह प्रतीत होता है, या आप समस्या के बारे में एक अलग तरीके से जाना होगा? –

उत्तर

6

क्या यह pahole है?

#pragma warning(enable : 4820) 

उस बिंदु पर आप शायद अभी के उत्पादन उपभोग कर सकते हैं:

+1

यह वास्तव में * बिल्कुल * क्या 'पैहोल' करता है, हालांकि यह स्रोत के बजाय डीबगिंग जानकारी के साथ संकलित द्विआधारी पर संचालित होता है। – caf

+0

+1 यह वही है जो मैं ढूंढ रहा था लेकिन नहीं मिला ... – bstpierre

-1

मुझे विश्वास नहीं है कि किसी भी सामान्य उद्देश्य की सुविधा सी में आत्मनिरीक्षण/प्रतिबिंब के लिए मौजूद है। यही जावा या सी # है।

+0

यह उत्तर सही प्रतीत होता है, तो इसे क्यों कम किया गया था? –

+0

यह संभवतः डाउनवॉटेड था क्योंकि इसने इस सवाल में योगदान नहीं दिया था। सिर्फ इसलिए कि कोई आत्मनिरीक्षण नहीं है इसका मतलब यह नहीं है कि पैडिंग खोजने का कोई तरीका नहीं है, क्योंकि कई उपयोगी उत्तर (और मेरा कामकाजी कार्यक्रम) दिखाते हैं। –

+0

@Desert: यह मामला नहीं है। यह एक ऐसे उपयोगकर्ता द्वारा एक-दूसरे के सेकंड के भीतर तेरह उत्तरों में से एक था जिसे मैंने नाराज किया था। –

-1

संरचना के सदस्यों के माध्यम से पुन: प्रयास करने के लिए कोई सी ++ भाषा सुविधा नहीं है, इसलिए मुझे लगता है कि आप भाग्य से बाहर हैं।

आप मैक्रो के साथ बॉयलर-प्लेट में से कुछ को काट सकते हैं, लेकिन मुझे लगता है कि आप सभी सदस्यों को स्पष्ट रूप से निर्दिष्ट कर रहे हैं।

+0

यह उत्तर सही प्रतीत होता है, तो इसे क्यों कम किया गया था? –

3

मैं एक बफर में पढ़ना और लिखना पसंद करता हूं, फिर फ़ंक्शन को बफर से संरचना सदस्यों को लोड करना होता है। यह संरचना में सीधे पढ़ने या memcpy का उपयोग करने से अधिक पोर्टेबल है। इसके अलावा यह एल्गोरिदम कंपाइलर पैडिंग के बारे में किसी भी चिंता को मुक्त करता है और एंडियनस को संभालने के लिए भी समायोजित किया जा सकता है।

एक सही और मजबूत कार्यक्रम बाइनरी डेटा को कॉम्पैक्ट करने में किसी भी समय से अधिक मूल्यवान है।

+0

दुर्भाग्य से मेरे पास मौजूदा प्रोग्राम में कोई भी कहना नहीं है जिसे यह मदद करने के लिए डिज़ाइन किया गया है, इसलिए मैं केवल बफर का उपयोग नहीं कर सकता। मेरा लक्ष्य डेटा संरचनाओं को बदलने पर पेश की गई त्रुटियों को कम करना है। वर्तमान में, डेटा संरचना में परिवर्तन एक गैर-अनुमानित तरीके से सभी निम्नलिखित संरचना सदस्यों के बाइट ऑफसेट को बदल देता है। मैं बस कुछ भविष्यवाणी करने की कोशिश कर रहा हूं। –

1

क्या आपका टूल फ़ील्ड के नाम ढूंढने के लिए संरचना परिभाषा को पार्स करता है, फिर सी कोड उत्पन्न करता है जो स्ट्रक्चर पैडिंग के विवरण को प्रिंट करता है, और आखिरकार संकलित करता है और उस कोड को चलाता है। दूसरे भाग के लिए पर्ल कोड नमूना:

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 

सी पार्स करने के लिए बहुत मुश्किल है, लेकिन आप केवल भाषा का एक बहुत छोटा हिस्सा में रुचि रखते हैं, और यह लग रहा है कि आप अपने स्रोत फ़ाइलों पर कुछ नियंत्रण है की तरह, तो एक साधारण पार्सर चाल करना चाहिए। उम्मीदवारों के रूप में सीपीएएन की खोज Devel::Tokenizer::C और कुछ C:: मॉड्यूल बदलती है (मुझे उनके नामों के अलावा उनके बारे में कुछ भी नहीं पता है)। यदि आपको वास्तव में सटीक सी पार्सर की आवश्यकता है, तो Cil है, लेकिन आपको ओकंपल में अपना विश्लेषण लिखना होगा।

2

आप एक सीपीएएन मॉड्यूल का उपयोग करने या अपने आप को हैकिंग करने के बजाय अपनी स्रोत फ़ाइलों को पार्स करने के लिए Exuberant Ctags का उपयोग कर सकते हैं। उदाहरण के लिए, निम्न कोड के लिए:

 
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: 

, पहले चौथे, और पांचवें स्तंभ पर्याप्त होना चाहिए आप यह निर्धारित करने के लिए क्या प्रकार मौजूद struct और क्या अपने सदस्यों कर रहे हैं। आप उस जानकारी का उपयोग सी प्रोग्राम उत्पन्न करने के लिए कर सकते हैं जो निर्धारित करता है कि प्रत्येक संरचना प्रकार में कितना पैडिंग है।

2

आप pstruct को आजमा सकते हैं।

मैंने कभी इसका उपयोग नहीं किया है, लेकिन मैं कुछ तरीकों की तलाश में था कि आप स्टैब्स का उपयोग करने में सक्षम हो सकते हैं और ऐसा लगता है कि यह बिल फिट होगा।

यदि ऐसा नहीं होता है, तो मैं स्टैब्स जानकारी को पार्स करने के अन्य तरीकों को देखने का सुझाव दूंगा।

+0

@daxim - फिक्स – bstpierre

+0

pstruct के लिए धन्यवाद एक अच्छा सुझाव है, और कुछ बुनियादी सी फाइलों के लिए काम करता है जो मैंने इसे फेंक दिया है। यह भी ज्यादातर मशीनों पर पहले से स्थापित होने का लाभ है। यद्यपि नेस्टेड structs के साथ कुछ परेशानी हो रही है। –

1

आप विजुअल C++ के लिए उपयोग किया है, तो आप संकलक थूक से बाहर करने के लिए जहां और कितना गद्दी जोड़ा गया है निम्न pragma जोड़ सकते हैं cl.exe और पार्टी जाओ।

4

आप कहो निम्नलिखित module.h:

#! /usr/bin/perl 

use warnings; 
use strict; 

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

structs के साथ, हम ctags करने के लिए और से हैडर फ़ीड:

typedef void (*handler)(void); 

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

struct bar { 
    float y; 
    short z; 
}; 

एक पर्ल unpack टेम्पलेट्स उत्पन्न करने के लिए कार्यक्रम प्रथागत सामने बात के साथ शुरू होता इसके उत्पादन संरचना सदस्यों को इकट्ठा करते हैं। परिणाम एक हैश है जिनकी चाबियाँ structs के नाम हैं और जिनके मान [$member_name, $type] रूप के जोड़े के सरणी हैं।

ध्यान दें कि यह केवल कुछ सी प्रकारों को संभालता है।

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 एक सी प्रोग्राम है जो मानक आउटपुट में ऑफसेट प्रिंट, डमी मूल्यों के साथ structs भरता उत्पन्न करता है, और मानक उत्पादन अपने-अपने आकार से पहले करने के लिए कच्चे संरचनाओं लिखते हैं बाइट्स में

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

जहां पैरामीटर $members कि ऑफसेट (यानी, प्रपत्र [$member_name, $type, $offset] की arrayrefs साथ संवर्धित किया गया है हैश structs द्वारा वापस में एक मूल्य है unpack के लिए खाका तैयार करें:

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

अब हम उत्पन्न कार्यक्रम के उत्पादन को पढ़ने और structs डिकोड:

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

संदर्भ के लिए, सी 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; 
} 
+0

कई निर्भरताओं के बिना एक महान स्पष्टीकरण और स्टैंड स्टैंड प्रोग्राम के लिए धन्यवाद। क्या यह नेस्टेड structs संभालता है? –

+0

@ डिज़ार्ट एड लिखित के रूप में, यह केवल कुछ आंतरिक प्रकार के सदस्यों के साथ फ्लैट संरचनाओं को संभालता है। नेस्टेड structs को संभालने में अधिक कुछ नहीं लगेगा: 'टेम्पलेट' को खुद को कॉल करने की आवश्यकता होगी जब यह किसी अन्य प्रकार की संरचना को देखे, और निश्चित रूप से 'structs' को स्वीकार किए जाने वाले प्रकारों में अधिक उदार होने की आवश्यकता होगी। –

+0

वाह, एक प्रश्न में सबसे लंबा जवाब है कि उनमें से एक नहीं है "आपका पसंदीदा ___ क्या है?" – mrk

संबंधित मुद्दे