In this situation, I pass a pointer from Go to a C function, the C function modifies that pointer value (fills in an array) and I use the same pointer again on the Go code, making sure to call C.free to release it after I am done.
I am sometimes getting a nil reference on that pointer, I am having a hard time understanding why.
Here, the object in Go is cpuTimesC, in C it is the cputicks.
I have also tried making the function return the pointer, with the same results. The weirdest thing is that if I put a printf statement at the end of the function, it takes longer before I eventually get the nil error.
package collector
/*
#cgo LDFLAGS: -lperfstat
#include <stdlib.h>
#include <stdio.h>
#include <libperfstat.h>
#include <string.h>
#include <time.h>
u_longlong_t **ref;
int getCPUTicks(uint64_t **cputicks, size_t *cpu_ticks_len) {
int i, ncpus, cputotal;
perfstat_id_t firstcpu;
perfstat_cpu_t *statp;
cputotal = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
if (cputotal <= 0){
return -1;
}
statp = calloc(cputotal, sizeof(perfstat_cpu_t));
if(statp==NULL){
return -1;
}
ncpus = perfstat_cpu(&firstcpu, statp, sizeof(perfstat_cpu_t), cputotal);
*cpu_ticks_len = ncpus*4;
*cputicks = (uint64_t *) malloc(sizeof(uint64_t)*(*cpu_ticks_len));
for (i = 0; i < ncpus; i++) {
int offset = 4 * i;
(*cputicks)[offset] = statp[i].user;
(*cputicks)[offset+1] = statp[i].sys;
(*cputicks)[offset+2] = statp[i].wait;
(*cputicks)[offset+3] = statp[i].idle;
}
return 0;
}
*/
import "C"
import (
"errors"
"unsafe"
"fmt"
"github.com/prometheus/client_golang/prometheus"
)
const ClocksPerSec = float64(C.CLK_TCK)
const maxCPUTimesLen = 1024 * 4
type statCollector struct {
cpu *prometheus.Desc
}
func init() {
registerCollector("cpu", true, NewCPUCollector)
}
func NewCPUCollector() (Collector, error) {
return &statCollector{
cpu: nodeCPUSecondsDesc,
}, nil
}
func (c *statCollector) Update(ch chan<- prometheus.Metric) error {
var fieldsCount = 4
cpuFields := []string{"user", "sys", "wait", "idle"}
var (
cpuTimesC *C.uint64_t
cpuTimesLength C.size_t
)
if C.getCPUTicks(&cpuTimesC, &cpuTimesLength) == -1 {
return errors.New("could not retrieve CPU times")
}
defer C.free(unsafe.Pointer(cpuTimesC))
cput := (*[maxCPUTimesLen]C.u_longlong_t)(unsafe.Pointer(cpuTimesC))[:cpuTimesLength:cpuTimesLength]
cpuTicks := make([]float64, cpuTimesLength)
for i, value := range cput {
cpuTicks[i] = float64(value) / ClocksPerSec
}
for i, value := range cpuTicks {
cpux := fmt.Sprintf("CPU %d", i/fieldsCount)
ch <- prometheus.MustNewConstMetric(c.cpu, prometheus.CounterValue, value, cpux, cpuFields[i%fieldsCount])
}
return nil
}
The error is:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x33 addr=0x0 pc=0x1003fcec0]
goroutine 940 [running]:
github.com/dlopes7/aix-prometheus-exporter/collector.(*statCollector).Update(0xa000100000d21b8, 0xa0001000028c480, 0x0, 0x0)
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/cpu_aix.go:81 +0xf0
github.com/dlopes7/aix-prometheus-exporter/collector.execute(0x10043e7e7, 0x3, 0x1101120e0, 0xa000100000d21b8, 0xa0001000028c480)
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/collector.go:95 +0x6c
github.com/dlopes7/aix-prometheus-exporter/collector.AIXCollector.Collect.func1(0xa0001000028c480, 0xa000100000cd440, 0x10043e7e7, 0x3, 0x1101120e0, 0xa000100000d21b8)
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/collector.go:115 +0x4c
created by github.com/dlopes7/aix-prometheus-exporter/collector.AIXCollector.Collect
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/collector.go:114 +0xf8
And I know it happens here, because for some reason cpuTimesC is nil:
cput := (*[maxCPUTimesLen]C.u_longlong_t)(unsafe.Pointer(cpuTimesC))[:cpuTimesLength:cpuTimesLength]
Why would this object be nil sometimes, and how do I make it remain in memory until I call that C.free?
This is on AIX PPC64 with cgo if that makes any difference.
This had nothing to do with pointers being removed from memory, the cputicks was NULL because ncpus was -1 so the malloc call returned NULL, ncpu was -1 because the first parameter of the perfstat_cpu call was not initialized properly, adding:
strcpy(firstcpu.name, FIRST_CPU);
Before the call to perfstat_cpu fixed the issue, as suggested by #kostik
Related
My XDP module compiles by itself, but compilation within the context of a Go program fails because it can't find bpf/bpf_helpers.h file. Here's the code leading up the problem:
package main
import (
"fmt"
bpf "github.com/iovisor/gobpf/bcc"
log "github.com/sirupsen/logrus"
"io/ioutil"
"os"
)
/*
#cgo CFLAGS: -I/usr/include/bcc/compat
#cgo LDFLAGS: -lbcc
#include <bcc/bcc_common.h>
#include <bcc/libbpf.h>
void perf_reader_free(void *ptr);
*/
import "C"
func main() {
// Get the source code from disk
source, err := ioutil.ReadFile("xdp/collect_ips.c")
if err != nil {
log.Fatalln("Cannot read collect_ips.c")
}
// Compile module
module := bpf.NewModule(string(source), []string{
"-Wall",
"-O2",
})
defer module.Close()
// Load module
fn, err := module.Load("collect_ips", C.BPF_PROG_TYPE_XDP, 1, 65536) // Problem happens here
// ...
The Go program compiles fine, but I when I run the program, I get this:
/virtual/main.c:2:10: fatal error: 'bpf/bpf_helpers.h' file not found
#include <bpf/bpf_helpers.h>
^~~~~~~~~~~~~~~~~~~
9 warnings and 1 error generated.
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4b23d4]
goroutine 1 [running]:
github.com/iovisor/gobpf/bcc.(*Module).Close.func1(0x203000)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:132 +0x14
github.com/iovisor/gobpf/bcc.(*Module).Close(0x0)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:132 +0x36
panic({0x4da720, 0x59ec60})
/usr/lib/go-1.17/src/runtime/panic.go:1038 +0x215
github.com/iovisor/gobpf/bcc.(*Module).Load(0x0, {0x4ef9f2, 0xb}, 0x2, 0x2, 0x8001)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:202 +0x36
main.main()
/home/me/go/src/code.squarespace.net/net/prism-stream/cmd/server/main.go:35 +0x1b7
This problem is happening because of my XDP module because if I comment out this header file in the C source, the error moves to a different header file.
I think this is happening because of bpf_helpers.h does not exist here https://github.com/iovisor/gobpf/tree/master/elf/include. If this is the issue, is there a way to use the header file from /usr/include/bpf?
If I take out bpf_helpers.h from the XDP code, I get an error complaining about the use of SEC in my code:
struct bpf_map_def SEC("maps") addr_map = {
.type = BPF_MAP_TYPE_LRU_HASH,
.key_size = sizeof(struct addr_desc_struct),
.value_size = sizeof(long),
.max_entries = 4096
};
I copied the macro for SEC from bpf_helpers.h to my code, but I then get error: variable has incomplete type 'struct bpf_map_def'. I also use bpf_map_lookup_elem() and bpf_map_update_elem(), which are defined in the bpf/ directory.
I used the eBPF library from Cilium. Here's an example program that loads .o file to an interface, waits for 10 seconds, and prints whatever is in the BPF map.
You have to make sure your XDP function has SEC("xdp") before it. Whereas if you load the program using xdp-loader, you can pass whatever you want to SEC as long as it's different from the name of the function.
package main
import (
"bytes"
"fmt"
"github.com/cilium/ebpf"
log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"time"
)
func main() {
var objects struct {
XCProg *ebpf.Program `ebpf:"collect_ips"` // Name of function containing the XDP program
XCMap *ebpf.Map `ebpf:"addr_map"` // Name of the map
}
spec, err := ebpf.LoadCollectionSpec("dist/collect_ips.o")
if err != nil {
log.Fatalln("ebpf.LoadCollectionSpec", err)
}
if err := spec.LoadAndAssign(&objects, nil); err != nil {
log.Fatalln("ebpf.LoadAndAssign", err)
}
link, err := netlink.LinkByName("enp0s8")
if err != nil {
log.Fatalln("netlink.LinkByName", err)
}
err = netlink.LinkSetXdpFdWithFlags(link, objects.XCProg.FD(), 2)
if err != nil {
log.Fatalln("netlink.LinkSetXdpFdWithFlags")
}
time.Sleep(time.Second*10)
var keyBytes, valueBytes []byte
iterator := objects.XCMap.Iterate()
if iterator == nil {
log.Fatalln("nil iterator")
}
for iterator.Next(&keyBytes, &valueBytes) != false {
fmt.Printf("%x: %x", keyBytes, valueBytes)
}
defer objects.XCProg.Close()
defer objects.XCMap.Close()
}
using cgo I am calling c function. I ran into situation where I have to copy C.int address into C.char[4]. Can I do that in go?
code snippet C- Structure:
struct data
{
char *a_add;
unsigned int length;
}
Go-Code
func main() {
var d[3] C.data
var filedescriptor C.int
d[0].a_add = &filedescriptor
d[0].length = 4
}
The problem is a_add is a char*. But I need to pass int variable address. The c-code is a legacy code, and I can't fix datatype now. Other C modules uses it, and it's working in C with a warning. where as in go, it is error.
Is there any way that I can copy address of int variable into char* array in cgo.
Update:
I tried d[0].a_add = (*C.char)(unsafe.Pointer(&filedescriptor )),
getting Error:
panic: runtime error: cgo argument has Go pointer to Go pointer
What Am I missing?
One of the problems you are running into is that in a call into C code, you may not pass a pointer to a Go pointer. The variable filedescriptor is a C.int, but &filedescriptor is a Go pointer, so you cannot use that (or rather, you cannot use that in the a_add field as a value).
There is a great deal about your C code that is not clear to me, but you can probably use the code below. Note that this code may be overkill for your particular situation. It is not meant to be particularly efficient or good, just as clear as possible while being extremely flexible—for instance, it can read from and write to packed C structures.
package main
// #include <stdio.h>
// #include <stdlib.h>
// #include <string.h>
//
// struct data {
// char *a_add;
// unsigned int length;
// };
//
// void f(struct data *p) {
// printf("p->a_add = %p, p->length = %u\n", p->a_add, p->length);
// printf("p->a_add as an int: %d\n", *(int *)p->a_add);
// *(int *)p->a_add = 0x12345678;
// }
import "C"
import (
"fmt"
"unsafe"
)
const cIntSize = C.sizeof_int
// Produce a Go int64 from a C int. The caller passes the address
// of the C int.
func int64FromCInt(ci unsafe.Pointer) int64 {
// Get a slice pointing to the bytes of the C int.
sci := (*[cIntSize]byte)(ci)[:]
switch {
case cIntSize == unsafe.Sizeof(int64(0)):
var gi int64
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return gi
case cIntSize == unsafe.Sizeof(int32(0)):
var gi int32
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return int64(gi)
case cIntSize == unsafe.Sizeof(int(0)):
var gi int
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return int64(gi)
default:
panic("no Go integer size matches C integer size")
}
}
// Write C int (via an unsafe.Pointer) from Go int. The caller
// passes the address of the C int.
func writeCIntFromInt(gi int, ci unsafe.Pointer) {
// Get a slices covering the bytes of the C int.
sci := (*[cIntSize]byte)(ci)[:]
switch {
case cIntSize == unsafe.Sizeof(gi):
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sci, sgi)
case cIntSize == unsafe.Sizeof(int64(0)):
// Copy value to int64 for copying purposes.
// Since int64 holds all int values, this always works.
gi2 := int64(gi)
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
copy(sci, sgi)
case cIntSize == unsafe.Sizeof(int32(0)):
// Copy value to int32 for copying purposes.
// Panic if we destroy the value via truncation.
gi2 := int32(gi)
if int(gi2) != gi {
panic(fmt.Sprintf("unable to send Go value %x to C: size of Go int=%d, size of C int=%d", gi, unsafe.Sizeof(gi), cIntSize))
}
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
copy(sci, sgi)
default:
panic("no Go integer size matches C integer size")
}
}
func main() {
b := C.malloc(cIntSize)
defer C.free(b)
writeCIntFromInt(32767, b)
d := C.struct_data{a_add: (*C.char)(b), length: cIntSize}
fmt.Println("calling C.f(d)")
C.f(&d)
result := int64FromCInt(unsafe.Pointer(d.a_add))
fmt.Printf("result = %#x\n", result)
}
I want to return an array to C caller, just like below, how to do it?
//export EtcdGetAllNodes
func EtcdGetAllNodes()[]uint32 {
a := []uint32{1,2,3}
return a
}
This function EtcdGetAllNodes will try to get value with a prefix for the specific key from etcd, it will return multiple values. How to return these values to C caller?
Command cgo
Passing pointers
A Go function called by C code may not return a Go pointer (which
implies that it may not return a string, slice, channel, and so
forth). A Go function called by C code may take C pointers as
arguments, and it may store non-pointer or C pointer data through
those pointers, but it may not store a Go pointer in memory pointed to
by a C pointer. A Go function called by C code may take a Go pointer
as an argument, but it must preserve the property that the Go memory
to which it points does not contain any Go pointers.
I want to return an array to C caller.
//export EtcdGetAllNodes
func EtcdGetAllNodes() []uint32 {
a := []uint32{1, 2, 3}
return a
}
A Go function called by C code may not return a Go pointer (which implies that it may not return a slice).
There are many possible solutions: Command cgo.
For example, here is one simple solution:
Output:
$ go build -buildmode=c-archive -o cmem.a cmem.go
$ gcc -pthread -o cmem cmem.c cmem.a
$ ./cmem
-- EtcdGetAllNodes --
nodes: 3
node 0: 1
node 1: 2
node 2: 3
$ echo $?
0
$
cmem.go:
package main
/*
#include <stdint.h>
#include <stdlib.h>
*/
import "C"
import "unsafe"
// toC: Go slice to C array
// c[0] is the number of elements,
// c[1] through c[c[0]] are the elements.
// When no longer in use, free the C array.
func toC(a []uint32) *C.uint32_t {
// C array
ca := (*C.uint32_t)(C.calloc(C.size_t(1+len(a)), C.sizeof_uint32_t))
// Go slice of C array
ga := (*[1 << 30]uint32)(unsafe.Pointer(ca))[: 1+len(a) : 1+len(a)]
// number of elements
ga[0] = uint32(len(a))
// elements
for i, e := range a {
ga[1+i] = e
}
return ca
}
//export EtcdGetAllNodes
// EtcdGetAllNodes: return all nodes as a C array.
// nodes[0] is the number of node elements.
// nodes[1] through nodes[nodes[0]] are the node elements.
// When no longer in use, free the nodes array.
func EtcdGetAllNodes() *C.uint32_t {
// TODO: code to get all etcd nodes
a := []uint32{1, 2, 3}
// nodes as a C array
return toC(a)
}
func main() {}
cmem.c:
#include "cmem.h"
#include <stdint.h>
#include <stdio.h>
int main() {
printf("-- EtcdGetAllNodes --\n");
// nodes[0] is the number of node elements.
// nodes[1] through nodes[nodes[0]] are the node elements.
// When no longer in use, free the nodes array.
uint32_t *nodes = EtcdGetAllNodes();
if (!nodes) {
return 1;
}
printf("nodes: %d\n", *nodes);
for (uint32_t i = 1; i <= *nodes; i++) {
printf("node %d: %d\n", i-1,*(nodes+i));
}
free(nodes);
return 0;
}
I am trying to load a BPF program using the bpf syscall but I am receiving invalid argument (EINVAL) on return. From the man page, the possible reasons for this are:
EINVAL
For BPF_PROG_LOAD, indicates an attempt to load an invalid program.
eBPF programs can be deemed invalid due to unrecognized instructions,
the use of reserved fields, jumps out of range, infinite loops or calls
of unknown functions.
So it seems there is something wrong with my BPF program. My BPF program is as follows:
#include <uapi/linux/bpf.h>
int prog(struct pt_regs *ctx)
{
return 0;
}
Which surely cannot have anything wrong with it.
I am compiling with the Makefile here (I removed most of the code from test_overhead_kprobe_kern.c to give a very simple program for testing).
What could be wrong with my program that caused it to get rejected?
uname -a: Linux ubuntu1710 4.13.0-32-generic #35-Ubuntu SMP Thu Jan 25 09:13:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
My full userspace code (in Go) is as follows:
package main
/*
#include <stdio.h>
#include <stdlib.h>
void print(char* s) {
printf("%s\n", s);
}
*/
import "C"
import (
"unsafe"
"golang.org/x/sys/unix"
"github.com/cilium/cilium/pkg/bpf"
)
import (
"fmt"
"io/ioutil"
)
const (
bufferSize = 256
sessionIDHTTPHeader = "X-Session-ID"
defaultServerAddress = "localhost"
defaultPort = 5050
)
const (
BPF_PROG_TYPE_UNSPEC = 0
BPF_PROG_TYPE_SOCKET_FILTER = 1
BPF_PROG_TYPE_KPROBE = 2
BPF_PROG_TYPE_SCHED_CLS = 3
BPF_PROG_TYPE_SCHED_ACT = 4
)
type ttyWrite struct {
Count int32
Buf [bufferSize]byte
SessionID int32
}
func main() {
//for i := 0; i < 6; i++ {
//b, err := ioutil.ReadFile(fmt.Sprintf("bpf/test%d.o", i))
b, err := ioutil.ReadFile("bpf/bpf_tty.o")
if err != nil {
fmt.Print(err)
}
err = loadProgram(BPF_PROG_TYPE_KPROBE, unsafe.Pointer(&b), len(b))
if err != nil {
fmt.Printf("%s\n", err)
}
//}
}
func loadProgram(progType int, insns unsafe.Pointer, insnCnt int) error {
licenseBuf := "GPL"
licenseStr := C.CString(licenseBuf)
defer C.free(unsafe.Pointer(licenseStr))
logStr := C.CString("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
defer C.free(unsafe.Pointer(logStr))
lba := struct {
progType uint32
//pad0 [4]byte
insnCnt uint32
//pad1 [4]byte
insns uint64
license uint64
logLevel uint32
//pad2 [4]byte
logSize uint32
//pad3 [4]byte
logBuf uint64
kernVersion uint32
//pad4 [4]byte
}{
progType: uint32(progType),
insnCnt: uint32(insnCnt),
insns: uint64(uintptr(insns)),
license: uint64(uintptr(unsafe.Pointer(licenseStr))),
logLevel: uint32(1),
logSize: uint32(50),
logBuf: uint64(uintptr(unsafe.Pointer(logStr))),
//logBuf: uint64(uintptr(unsafe.Pointer(bufStr))),
// /usr/src/linux-headers-4.13.0-32-generic/include/generated/uapi/linux/version.h
kernVersion: uint32(265485),
}
ret, _, err := unix.Syscall(
unix.SYS_BPF,
bpf.BPF_PROG_LOAD,
uintptr(unsafe.Pointer(&lba)),
unsafe.Sizeof(lba),
)
//fmt.Printf("%s\n", logBuf)
//cs := C.CString("XXXXXXXXXX")
C.print(logStr)
//fmt.Printf("%c\n", *logStr)
if ret != 0 || err != 0 {
//fmt.Printf("%#v %d\n", logBuf, unsafe.Sizeof(lba))
return fmt.Errorf("Unable to load program: ret: %d: %s", int(ret), err)
}
return nil
}
As Qeole pointed out in your previous question, your userspace Go program needs to extract the BPF instructions (.text section) from the object file. Otherwise, the kernel will try to interpret the binary content as BPF instructions and inevitably fail.
I have recently begun working with LLVM. I am trying to write a pass in LLVM that given the following code
string = (char *)malloc(100);
string = NULL;
and the corresponding LLVM IR
%call = call noalias i8* #malloc(i64 100) #3
store i8* %call, i8** %string, align 8
store i8* null, i8** %string, align 8
detects instructions calling malloc, extracts number of bytes assigned (in this case 100), the address returned and the variable name that the address is assigned to.
std::map<std::string, std::tuple<size_t, int> > mem_addrs; // stores pointer name, address and no. of bytes allocated
Count() : ModulePass(ID) {}
virtual bool runOnModule(Module &M) {
for (Function &F: M) {
for (BasicBlock &B: F) {
for (Instruction &I: B) {
if(CallInst* call_inst = dyn_cast<CallInst>(&I)) {
Function* fn = call_inst->getCalledFunction();
StringRef fn_name = fn->getName();
errs() << fn_name << " : " << "\n";
for(auto args = fn->arg_begin(); args != fn->arg_end(); ++args) {
ConstantInt* arg = dyn_cast<ConstantInt>(&(*args));
if (arg != NULL)
errs() << arg->getValue() << "\n";
}
}
}
}
}
The output is
-VirtualBox:~/program_analysis$ opt -load $LLVMLIB/CSE231.so -analyze -count < $BENCHMARKS/leaktest/leaktest.bc > $OUTPUTLOGS/welcome.static.log
ok
allocaimw
allocaleak
allocamalloc : 0x2f5d9e0
0 opt 0x0000000001315cf2 llvm::sys::PrintStackTrace(_IO_FILE*) + 34
1 opt 0x0000000001315914
2 libpthread.so.0 0x00007f0b53f12330
3 opt 0x00000000012ec78f llvm::APInt::toString(llvm::SmallVectorImpl<char>&, unsigned int, bool, bool) const + 79
4 opt 0x00000000012ed309 llvm::APInt::print(llvm::raw_ostream&, bool) const + 57
5 CSE231.so 0x00007f0b52f16661
6 opt 0x00000000012ad6cd llvm::legacy::PassManagerImpl::run(llvm::Module&) + 797
7 opt 0x000000000058e190 main + 2752
8 libc.so.6 0x00007f0b5313af45 __libc_start_main + 245
9 opt 0x00000000005ab2ca
Stack dump:
0. Program arguments: opt -load /home/hifza/program_analysis/llvm/build/Release+Asserts/lib/CSE231.so -analyze -count
1. Running pass 'Instruction Counts Pass' on module '<stdin>'.
Segmentation fault (core dumped)
I am able to detect malloc instructions, but I am not able to find out the corresponding memory address and the number of bytes assigned. Can anyone guide me on how can I go about doing this? Thanks.
You don't check the result of dyn_cast<ConstantInt>(&(*args)). If casted type is not a ConstantInt, it returns nullptr. And in the next line (arg->getValue()) you dereference it.
I prefer detecting malloc calls,
by first detecting store insts
then checking whether LHS is a pointer
then find out what is RHS (by using a stack approach to find actual value, since LLVM IR is a load-store architecture and hence we don't find the actual value in RHS, always)
if I end up getting a call inst then
check whether its malloc or not
Once you have detected the malloc, you can simply fetch the bytes accessed by ip->getOperand(0)
And the variable name pointing to the memory is nothing but the value returned by Store inst that you just started with - lhs in the code
Am sharing the code snippet,which will also work for inter-procedural cases as well and also supports new operator .
void findOperand(Value *itVal) {
std::stack<Value *> st;
st.push(itVal);
while(!st.empty()) {
auto ele = st.top();
st.pop();
if(isa<Instruction>(ele)) {
Instruction *tip = (Instruction *)ele;
if(isa<AllocaInst>(tip)) {
errs()<<"others\n";
//opdSet.insert(ele);
}else if(isa<LoadInst>(tip)) {
Value *ti = tip->getOperand(0);
if(!isa<ConstantData>(ti))
st.push(ti);
} else if(isa<CallInst>(tip)) {
Function *calledFp = cast<CallInst>(tip)->getCalledFunction();
errs()<<calledFp->getName()<<"\n";
if(calledFp->getName() == "malloc" || calledFp->getName() == "_Znwm") {
errs()<<"Dynamic memory allocation!\n";
errs()<<tip->getNumOperands()<<"\n";
errs()<<tip->getOperand(0)<<"\n";
} else {
//fetch the last bb of the function
auto bb = calledFp->end();
if(bb != calledFp->begin()) {
bb --;
BasicBlock *bp = &(*bb);
//fetch the terminator
Instruction *term = bp->getTerminator();
if(isa<ReturnInst>(term)) {
//find Operand
findOperand(term->getOperand(0));
errs()<<"done\n";
}
}
}
} else {
for(int i=0, numOp = tip->getNumOperands(); i < numOp; i++) {
Value *ti = tip->getOperand(i);
if(!isa<ConstantData>(ti)) {
st.push(ti);
}
}
}
} else if (isa<GlobalVariable>(ele)) {
errs()<<"others\n";
}
}
}//findOperand
void visitStoreInst(StoreInst &ip) {
Value *lhs = ip.getOperand(1);
Value *rhs = ip.getOperand(0);
if(lhs->getType()->getContainedType(0)->isPointerTy()) {
//figure out rhs
errs()<<"pointer assignment!"<<lhs->getName()<<"\n";
findOperand(rhs);
}
}