Skip to content

Latest commit

 

History

History
213 lines (161 loc) · 3.74 KB

README.md

File metadata and controls

213 lines (161 loc) · 3.74 KB

clayout, translate C++/Rust type into C type with the same memory layout. Generally, clayout is used together with bpftrace.

clayout is developed on ddbug. THANKS FOR ddbug!

Usage

Imagine a scenario where you want to use bpftrace to track the value of S::x during the running of the following program.

#include <stdio.h>
#include <unistd.h>

struct X {
  virtual ~X() {}

  int x1;
};

struct S : public X {
  S() : x(0) {}

  S(const S &other) : x(other.x) {}

  S f(int y, int z) {
    printf("output from a.out: this.x=%d y=%d z=%d\n", x, y, z);
    x += (y + z);
    return *this;
  }

  int x;
};

int main(int argc, char **argv) {
  S s;
  int i = 0;
  while (1) {
    s.f(i, i);
    ++i;
    sleep(1);
    // break;
  }
  return 0;
}

clayout can translate S into a C structure with the same memory layout:

# clayout will generate struct.h, struct.c
$ clayout -i ${binary path} -o struct S
// struct.h
// Generated by hidva/clayout! 大吉大利!
#pragma once
#include <linux/types.h>
struct HidvaStruct2 {
  void** __mem1;
  int x1;
} __attribute__((__packed__));


struct S {
  struct HidvaStruct2 __parent0;
  int x;
} __attribute__((__packed__));

So you can easily write the following bpftrace script:

#include "struct.h"

u:/apsara/zhanyi.ww/tmp/bphtrace/x/trace:_ZN1S1fEii {
  printf("output from bpftrace: ret=%p this.x=%d y=%d z=%d\n", (int32*)arg0, ((struct S*)arg1)->x, arg2, arg3)
}
$ bpftrace  -c ./trace t.bt
Attaching 1 probe...
output from a.out: this.x=0 y=0 z=0
output from bpftrace: ret=0x7ffff3044610 this.x=0 y=0 z=0
output from a.out: this.x=0 y=1 z=1
output from bpftrace: ret=0x7ffff3044610 this.x=0 y=1 z=1

Please note that you may intuitively think that the layout of S is as follows:

struct X {
  void** __mem1;
  int x1;
}

struct S {
  struct X __parent0;
  int x;
}

But actually it is wrong! S::x will reuse the padding part of X in C++!

multi input

clayout supports multiple input files, and type references across files.

// x.h
struct X {
  virtual ~X();

  int x1;
};

struct S : public X {
  S();

  S(const S &other);

  S f(int y, int z);

  int x;
};

// X.cc
#include <stdio.h>
#include "x.h"

X::~X() {}

S::S(): x(0) {}

S::S(const S &other) : x(other.x) {}

S S::f(int y, int z) {
  printf("output from a.out: this.x=%d y=%d z=%d\n", x, y, z);
  x += (y + z);
  return *this;
}

// trace.cc
#include <unistd.h>
#include "x.h"

int main(int argc, char **argv) {
  S s;
  int i = 0;
  while (1) {
    s.f(i, i);
    ++i;
    sleep(1);
  }
  return 0;
}
$ clang++ -fPIC -shared -g -O0 X.cc -o libzh_x.so
$ clang++ -g -O0 trace.cc -o trace -L. -lzh_x

Because of -fstandalone-debug, the trace binary file does not contain any debugging information of X:

$ readelf --debug-dump=info trace
 <1><fc>: Abbrev Number: 13 (DW_TAG_structure_type)
    <fd>   DW_AT_name        : X
    <ff>   DW_AT_declaration : 1

Because there is no debugging information of X in the trace binary file, a placeholder __u8 __unknown_type1[12] is used.

$ clayout -i trace -o output S
// output.h
// Generated by hidva/clayout! 大吉大利!
#pragma once
#include <linux/types.h>

struct S {
  __u8 __unknown_type1[12];
  int x;
} __attribute__((__packed__));

We can use multi input file to get the detail of X:

$ clayout -i trace -i libzh_x.so -o output S
// output.h
// Generated by hidva/clayout! 大吉大利!
#pragma once
#include <linux/types.h>
struct HidvaStruct2 {
  void** __mem1;
  int x1;
} __attribute__((__packed__));


struct S {
  struct HidvaStruct2 __parent0;
  int x;
} __attribute__((__packed__));