eBPF, track values longer than stack size? - c

I'm extending a program which take arguments of traced function and print it. Everything works fine with numeric arguments and short strings. But it's not clear how to handle with long string that longer than stack size in eBPF (which is limited with 512 byte).
In example below, string is limited by 80 bytes, of course it can be increased up to 512, but how to track more longer strings?
Example of C program with traced function which called "ameba"
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
char * ameba(char * s1);
int main(void) {
printf("%s\n", ameba("verylonglongstring...1111111111111111111111111111111111111111111111111111111111111111111111111111"));
}
char * ameba(char * s1) {
char *s;
s = (char *) malloc(128);
sleep(1);
snprintf(s, 128, "ameba: %s", s1);
return s;
}
Example of Go code
package main
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"os"
"os/signal"
"unsafe"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct ameba_event_t {
u32 pid;
char comm[TASK_COMM_LEN];
char arg1[80];
} __attribute__((packed));
BPF_PERF_OUTPUT(ameba_events);
int get_input_args(struct pt_regs *ctx) {
struct ameba_event_t event = {};
if (!PT_REGS_PARM1(ctx))
return 0;
event.pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event.comm, sizeof(event.comm));
bpf_probe_read(&event.arg1, sizeof(event.arg1), (void *)PT_REGS_PARM1(ctx));
ameba_events.perf_submit(ctx, &event, sizeof(event));
return 0;
}
`
type amebaEvent struct {
Pid uint32
Comm [16]byte
Arg1 [80]byte
}
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
amebaUprobe, err := m.LoadUprobe("get_input_args")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load get_input_args: %s\n", err)
os.Exit(1)
}
err = m.AttachUprobe("/home/lesovsky/Git/sandbox/ameba", "ameba", amebaUprobe, -1)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to attach input_args: %s\n", err)
os.Exit(1)
}
table := bpf.NewTable(m.TableId("ameba_events"), m)
channel := make(chan []byte)
perfMap, err := bpf.InitPerfMap(table, channel)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to init perf map: %s\n", err)
os.Exit(1)
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, os.Kill)
fmt.Printf("%10s\t%s\t%s\n", "PID", "COMMAND", "ARG1")
go func() {
var event amebaEvent
for {
data := <-channel
err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event)
if err != nil {
fmt.Printf("failed to decode received data: %s\n", err)
continue
}
comm := (*C.char)(unsafe.Pointer(&event.Comm))
query := (*C.char)(unsafe.Pointer(&event.Query))
fmt.Printf("%10d\t%s\t%s\n", event.Pid, C.GoString(comm), C.GoString(query))
}
}()
perfMap.Start()
<-sig
perfMap.Stop()
}
I read about BPF_MAP_TYPE_PERCPU_ARRAY could help in such case, but it's not clear for me how to use it.
EDIT: Go program has been modified to using "C" and "unsafe".

I read about BPF_MAP_TYPE_PERCPU_ARRAY could help in such case, but it's not clear for me how to use it.
You're right. Peeps usually rely on per-cpu arrays to overcome stack size limitations. The following implements that sort of solution, whereby I'm using a per-cpu array to store struct ameba_event_t instead of storing it on the stack.
I think you're going to need Linux v4.18 to be able to do this (you need commit d71962f). I haven't tested the code as I don't have the right setup at hand, but can later if you run into any issue.
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct ameba_event_t {
u32 pid;
char comm[TASK_COMM_LEN];
char arg1[512];
} __attribute__((packed));
BPF_PERF_OUTPUT(ameba_events);
BPF_PERCPU_ARRAY(ameba_struct, struct ameba_event_t, 1);
int get_input_args(struct pt_regs *ctx) {
int zero = 0;
if (!PT_REGS_PARM1(ctx))
return 0;
struct ameba_event_t* event = ameba_struct.lookup(&zero);
if (!event)
return 0;
event->pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event->comm, sizeof(event->comm));
bpf_probe_read(&event->arg1, sizeof(event->arg1), (void *)PT_REGS_PARM1(ctx));
ameba_events.perf_submit(ctx, event, sizeof(*event));
return 0;
}

Related

C language how to make a timeout function using in gethostname()?

This program reads the domain from a file to a string, truncates the string with "\n" as a key, and then executes the getostbyname() function for each domain to enter the resulting information into the file.
When I use gethostbyname, I want to create a function to enter fail if there is no response for a certain period of time. I tried to implement and process the timeout function, but the function did not work.
The logic that I thought of is calling gethostbyname() and entering fail in the file if there is no response for two seconds, then proceed to the next string and call gethostbyname().
The amount of files is about 150 million domains, so I took only a part of them and made them an example.
Please help me create the functions I want.
This is input.txt
www.naver.com
kbphonemall.com
kbplant.com
k-bplus.com
kbpointreestore.com
kbprint.com
kbprism.com
kbprivatebanking.com
kbpstore.com
kbr9rtudaf5ppy.com
kbrafting.com
kbraille.com
kbrainbank.com
kbrainbow.com
kbrainc.com
kbrainglocal.com
kbrandexpo.com
kbrandingschool.com
kbrandmall.com
kbrandmuseum.com
kbranking.com
k-bread.com
k-breadshop.com
kbreaknews.com
kbreasy.com
kbr-easy.com
kb-rent.com
kbrentacar.com
kb-rentcar.com
kbreport.com
k-brew.com
kbrewtec.com
kbrick.com
kbridge21.com
kbrno.com
k-broad.com
kbrockstar.com
kbrosbeauty.com
kbrosis.com
kbrworks.com
kbs1004.com
kbs123.com
kbs20.com
kbs220.com
kbs3.com
kbs31.com
kbs336.com
kbs358.com
kbs40.com
kbs556.com
kbs5678.com
kbs69.com
kbs7.com
kbs700.com
kbs79.com
kbs87.com
kbs9003.com
kbs911.com
kbs97.com
kbsarchive.com
kbsavers.com
kbsavings.com
kbsavingstar.com
kbsba.com
kbsbaechoo.com
kbsbook.com
kbsc1.com
kbscarf.com
kbsccoding.com
kbscert.com
kbschoolbanking.com
kbscyerrutb.com
kbsec.com
kbsec-greeting.com
kbsecuritynews.com
kbseschool.com
kbsestudy.com
kbsfn.com
kbsgmp.com
kbsgreen.com
kbsgulbee.com
kbs-hip.com
kbsholdings.com
kbshsg.com
kbsisa.com
kbsjeju.com
kbskadt.com
kbskiotmall.com
kbsklt.com
kbskorea.com
kbskorean.com
kbskovo.com
kbsktw.com
kbsktwidea.com
kbslogos.com
kbslove.com
kbs-lse.com
kbsmc-ibd.com
kbsmcob.com
kbsmedi.com
kbsmetal.com
kbsmol.com
kbs-mt.com
kbsmt-academy.com
kbsnd.com
kbsnews.com
kbsnnews.com
kbsonbocns.com
kbsonbodirect.com
kbsonsa.com
kbspetdoc.com
kbsplus.com
kbspro.com
kbsq.com
kbssky.com
kbssomgr.com
kbssports.com
kbsstudy.com
kbstar.com
kbstar2020.com
kbstar21.com
kbstar24.com
kbstarb.com
kbstarcard.com
kbstarclub.com
kbstardirect.com
kbstarebank.com
kbstarfg.com
kbstarfinance.com
kbstarflower.com
kbstarfx.com
kbstarg.com
kbstarir.com
kbstarland.com
kbstarmail.com
kbstarmall.com
kbstarmbank.com
kbstarmoney.com
kbstarpb.com
kbstarsvc.com
kbstarworld.com
kbstarzone.com
kbstbt.com
kbsteel.com
kbstock1.com
kbstock5.com
kbs-travel.com
kbstructure.com
kbstv.com
kbstve.com
kbsuf.com
kbsupercard.com
kbsupporters.com
kbsv.com
kbsvoice.com
kbtac.com
kbtchain.com
kbtechgate.com
kbtechone.com
kbtechpos.com
kbtechshop.com
kbtilbo.com
kb-truck.com
kbtups.com
kbtva.com
kbubi.com
kbuddhism.com
k-buddhism.com
k-buddy.com
kbujejucall.com
kbulgyonews.com
k-bulls.com
kbumall.com
kbund.com
kb-uniform.com
kbusinessnews.com
kbusinfo.com
kbusking.com
kbuvd.com
kbvalve.com
kbvipstock.com
kbweb.com
kbweddingfair.com
kbwel.com
kbwith.com
kbwlab.com
kbwnet.com
kb-world.com
kbworldwide.com
kb-world-wide.com
kby8992.com
kbynews.com
kc005.com
kc04.com
kc0522.com
kc1004.com
kc114.com
kc1357.com
kc1895.com
kc1904.com
kc22.com
kc25.com
kc712.com
kc9479.com
kc97bkyg3tvzgdws.com
kca2009.com
kca21.com
kca7.com
kcaa1.com
kcaa12.com
kcaa21.com
kca-academy.com
kcaawelfare.com
kcabiz.com
kca-charcoal.com
kcadcom.com
k-caddie.com
k-cae.com
kcafrp.com
kcagcf.com
kcagolf.com
kcagym.com
kcahub.com
kcairfiltertech.com
kcakca.com
kcakn.com
kcakorea.com
kcakyrgyz.com
kcallica.com
kcaltrip.com
kcangle.com
kcar.com
k-car.com
kcar2.com
kcaratpay.com
kcarauction.com
kcaraudio.com
kcarb2b.com
kcarcapital.com
kcar-capital.com
kcard.com
kcardirect.com
kcar-direct.com
k-cardirect.com
k-careeraptitude.com
kcarfriends.com
kcarlogis.com
kcarmall.com
kcarnet.com
kcarschool.com
kcarz.com
k-cata.com
kcateam.com
kcatfree.com
kca-therapy.com
kcb4u.com
kcb5.com
kcbc-acc.com
kcbcard.com
kcbec.com
kcbend.com
kcbetrade.com
kcbexchange.com
kcbiea.com
kc-biz.com
kcboltec.com
kcbs119.com
kcbtec.com
kcbtv.com
kcbunion.com
kcc000.com
kcc001.com
kcc002.com
kcc003.com
kcc007.com
kcc1004.com
kcc1122.com
kcc114.com
kcc2.com
kcc21.com
kcc258.com
kcc339.com
kcc5858.com
kcc775.com
kcc7777.com
kcc7979.com
kcc8282.com
kccaedu.com
kccardpos.com
kccarr.com
kccaster.com
kccasters.com
kccauto.com
kccce.com
kccchem.com
kccckorea.com
kcccolorndesign.com
kcccontainer.com
kccdeco.com
kccdepot.com
kccea.com
kccegis.com
kccei.com
kcceleader.com
kccenc.com
kccepost.com
kcceramice.com
kcceye.com
kccf2012.com
kccfilm.com
kccflooring.com
kccgift.com
kccglass.com
kccgroup.com
kcchain.com
kccheavyduty.com
kccholdings.com
kcchomecc.com
kcchomeccinterior.com
kcchwarang.com
kccia.com
kccichem.com
kccinsight.com
kccinterior.com
kccjewelry.com
kcckdong.com
kccks.com
kccmarine.com
kccmaul.com
kccnara.com
kccofficial.com
kccold.com
kccomang.com
kccond.com
kcconveyor.com
kc-coop.com
kc-cottrell.com
kccpaint114.com
kccpaintmall.com
kccplant.com
kccplus.com
kccpr.com
kccrefinish.com
kccrose.com
kccsa.com
kccscleaning.com
kccseoul.com
kccsilicon.com
kccsilicone.com
kccsmms.com
kccssafe.com
kccswitzen-patio.com
kcctm.com
kcctour.com
kccultural.com
kccview.com
kccvma.com
kccwelltztower.com
kccwindow.com
kccwindows.com
kccworld.com
kccycasters.com
kccyes.com
kcdc2020.com
kcdc21.com
kcdcode.com
kcdeaf.com
kcdfarm.com
kcdic.com
kcdkorea.com
kcdoll.com
kcdoor.com
k-cea.com
kcea98.com
kceasw.com
kceautodoor.com
kceccenter.com
kcecctv.com
kcecoin.com
kcecrs.com
and this is code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#define IPATH "example.txt"
#define OPATH "result/output.txt"
struct hostent *host;
int whatthe = 0;
void sig_handler(int signum){
host = NULL;
}
void dnslookup(char temp1[]){
host = gethostbyname(temp1);
}
int main(int argc, char *argv[]){
struct timeval tv;
double begin, end;
gettimeofday(&tv, NULL);
begin = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ;
char *ListBuffer;
int ListSize;
FILE *InputFile = fopen(IPATH, "r");
FILE *OutputFile = fopen(OPATH, "w");
fseek(InputFile, 0, SEEK_END);
ListSize = ftell(InputFile); //길이를 재고
ListSize ++; //NULL 문자가 들어갈 자리까지 해줌. 문자열의 끝을 알기 위해서
ListBuffer = malloc(ListSize); //잰 길이만큼 동적할당
memset(ListBuffer, 0, ListSize); //할당한 메모리 초기화
ListBuffer[ListSize] = '\0';
fseek(InputFile, 0, SEEK_SET);
fread(ListBuffer, ListSize, 1, InputFile); //읽어와서 동적할당한 배열에 넣기
char temp[] = {0, };
char *temp2;
int count = 0;
int h_count = 0;
char *str1 = NULL;
char *temp1 = strtok_r(ListBuffer, "\n", &str1);
while (temp1 != NULL){
dnslookup(temp1);
signal(SIGALRM,sig_handler);
alarm(2);
if (host != NULL){
fprintf(OutputFile, "%s / %s\n", temp1, inet_ntoa(*(struct in_addr *)host->h_addr_list[0]));
}
else{
fprintf(OutputFile, "%s / %s\n",temp1, "FAIL");
}
temp1 = strtok_r(NULL, "\n", &str1);
}
fclose(InputFile); //파일 포인터 닫기
fclose(OutputFile); //파일 포인터 닫기
free(ListBuffer); // 동적 메모리 해제
ListBuffer = NULL;
gettimeofday(&tv, NULL);
end = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ;
// 출력
printf("Execution time %f\n", (end - begin) / 1000);
}
what is problem?
If you need more explanation, I'll answer it quickly.
Thank you for reading my question. Have a nice day.
Use setjmp() & longjmp() pair with alarm().
#include <setjmp.h>
#define DNS_TIMEOUT 2 // timeout you need
static jmp_buf env;
void sig_handler (int signum) {
//host = NULL;
longjmp (env, 1); // jump to setjmp() location with value 1
}
int dnslookup (const char* domain) {
#define STATUS_TIMEOUT 128 // custom: timeout occurred
int status = 0;
signal (SIGALRM, sig_handler);
if (0 == setjmp(env)) { // to return here after timeout with return value 1
alarm (DNS_TIMEOUT); // setting timeout
host = gethostbyname (domain);
signal (SIGALRM, SIG_DFL);
alarm (0); // cancel alarm
} else { // after timeout-alarm we're here
// you may want to record TIMEOUT instead of FAIL
status = STATUS_TIMEOUT;
host = NULL;
//printf ("\nTimeout for %s", domain);
}
return status;
}
Also:
ListBuffer = malloc(ListSize); //잰 길이만큼 동적할당
memset(ListBuffer, 0, ListSize); //할당한 메모리 초기화 // not necessary
//ListBuffer[ListSize] = '\0'; // BUFFER-OVERFLOW
...
char *rmdList = NULL;
char *domain = strtok_r (ListBuffer, "\n", &rmdList);
while (domain != NULL) {
int status = dnslookup (domain);
// signal (SIGALRM, sig_handler);
// alarm (2);
if (host)
fprintf (OutputFile, "%s / %s\n", domain, inet_ntoa (* (struct in_addr *) host->h_addr_list[0]));
else if (STATUS_TIMEOUT == status)
fprintf (OutputFile, "%s / %s\n", domain, "TIMEOUT");
else
fprintf (OutputFile, "%s / %s\n", domain, "FAIL");
domain = strtok_r (NULL, "\n", &rmdList);
}
That said :
The gethostbyname*(), gethostbyaddr*(), herror(), and hstrerror()
functions are obsolete. Applications should use getaddrinfo(3),
getnameinfo(3), and gai_strerror(3) instead.

BPF tail call not called

In the following code BPF program tail_prog is not getting tail called from main_prog:
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct bpf_map_def SEC("maps") jump_table = {
.type = BPF_MAP_TYPE_PROG_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
.max_entries = 8,
};
SEC("xdp")
int main_prog(struct xdp_md *ctx) {
bpf_printk("Making tail call");
bpf_tail_call(ctx, &jump_table, 0);
return XDP_PASS;
}
SEC("xdp_1")
int tail_prog(struct xdp_md *ctx) {
bpf_printk("Inside tail call");
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
I observe only the print in main_prog is printed.
I'm loading the BPF programs using Cilium's eBPF Go package. Here's the code for loading the programs and maps:
type BpfObjects struct {
MainProg *ebpf.Program `ebpf:"main_prog"`
TailProg *ebpf.Program `ebpf:"tail_prog"`
JumpTable *ebpf.Map `ebpf:"jump_table"`
}
var objects BpfObjects
spec, err := ebpf.LoadCollectionSpec("prog.o")
if err != nil {
log.Fatalln("ebpf.LoadCollectionSpec", err)
}
if err := spec.LoadAndAssign(&objects, nil); err != nil {
log.Fatalln("ebpf.LoadAndAssign", err)
}
objects.JumpTable.Update(0, objects.TailProg.FD(), ebpf.UpdateAny)
According to this, the jump table has be initialized from user space, which is what I think the last line above is supposed to do. However, I don't see any difference whether that line is there or not.
I wasn't looking at the error returned from the Update function: Update can't marshal key: encoding int: binary.Write: invalid type int. Therefore, the program array map was not updated. I changed to the following:
err = objects.JumpTable.Update(uint32(0), uint32(objects.CopyHttpHostnameProg.FD()), ebpf.UpdateAny)
if err != nil {
println("Update", err.Error())
}
If you pass 0 as the key, the size of the key is 8 bytes, which is why you have to do uint32(0), which matches the map's definition. Now the tail calls succeed.

Why passing Go bytes to C, the bytes length doesn't match in cgo in this code below?

When passing golang bytes to C, the bytes length doesn't match.
The resulting strlen(key) and keylen doesnt match up.
Build with "go build file.go"
You can download the go file below here:
https://pastebin.com/raw/hnMfXJKq
<- does anyone know why my cgo call the []bytes to c has error? why the strlen doesnt match?
Expected output is supposed to be the same key length. Sometimes work, sometimes doesn't.
package main
/*
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
void replaceK(void *key, size_t keylen, void *value, size_t valuelen);
void replaceK(void *key, size_t keylen, void *value, size_t valuelen)
{
printf("replaceK : key = %s = %lu = %lu = %s = %lu\n",(char*) key,keylen,strlen(key),(char*) value,valuelen);
if (keylen != strlen(key)){
printf("ERROR!!! keylen : %lu != strlen(key) : %lu!!!\n",keylen,strlen(key));
exit(1);
}
}
*/
import "C"
import (
"fmt"
"unsafe"
"math/rand"
"time"
)
func Set(key,value []byte) {
cKey := cByteSlice(key)
cValue := cByteSlice(value)
C.replaceK(unsafe.Pointer(cKey),C.ulong(len(key)),unsafe.Pointer(cValue),C.ulong(len(value)))
C.free(unsafe.Pointer(cKey))
C.free(unsafe.Pointer(cValue))
}
func byteToChar(b []byte) *C.char {
var c *C.char
if len(b) > 0 {
c = (*C.char)(unsafe.Pointer(&b[0]))
}
return c
}
var letterRunes = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func RandStringRunes(n int) []byte {
randNum := rand.Intn(n)+1
b := make([]byte, randNum)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return b
}
func cByteSlice(b []byte) *C.char {
var c *C.char
if len(b) > 0 {
c = (*C.char)(C.CBytes(b))
}
return c
}
func main() {
rand.Seed(time.Now().UnixNano())
var key []byte
var value []byte
for i:=0;i<10000000;i++ {
key = RandStringRunes(10)
value = RandStringRunes(20)
randnum := 1
if randnum == 1 {
fmt.Printf(">>> = %s = %s\n",key, value)
Set(key,value)
}
}
}
The C strlen function is for use with null-terminated strings, not pointer+length strings.
You can print a pointer+length string with the C printf function using %.*s instead of %s. Since Go string and []byte variables both use the pointer+length encoding, that is probably the right way to print a string obtained from Go.

Trouble with implementing a Direct Module Method in Azure IoT Edge Module using azure-iot-sdk-c SDK

Full disclosure, I have asked this question on Azure IoT SDK C github project, but since they recommend looking on StackOverflow, I decided to post here as well.
I am having trouble implementing a Direct Module Method handler in my azure-iot-sdk-c based IoT Edge Module. I could not find a documentation page with an example implementation, so I assembled my implementation from various SDK documentation pages and unit test "examples".
To test this, I have a dedicated Linux based PC (Ubuntu 18.04) running iotedge 1.0.8-2. I can see that my module is starting and printing its version and firing the connection status callback message. I even even see that the ModuleTwin callback is firing and printing the payload when I manually edit the module identity twin for my device in the portal.
However, when I try to manually invoke a Direct Method on my module within my device in the portal, I see nothing printed and I get the following error in the portal:
{"message":"GatewayTimeout:{\r\n \"Message\": \"{\\\"errorCode\\\":504101,\\\"trackingId\\\":\\\"8215e001484d41a19245639844f44f78-G:9-TimeStamp:01/14/2020 21:20:42-G:0-TimeStamp:01/14/2020 21:20:42\\\",\\\"message\\\":\\\"Timed out waiting for the response from device.\\\",\\\"info\\\":{},\\\"timestampUtc\\\":\\\"2020-01-14T21:20:42.0556758Z\\\"}\",\r\n \"ExceptionMessage\": \"\"\r\n}"}
The relevant code is below. I looked on StackOverflow but examples there are not C SDK based. Where am I going wrong with Direct Module Methods? Thank you!
Update: An interesting observation is that if I change this code to use MQTT from AMQP, then everything works. Is AMQP not supported for Direct Module Methods?
#include <iothub_module_client_ll.h>
#include <iothub_client_options.h>
#include <iothub_message.h>
#include <azure_c_shared_utility/threadapi.h>
#include <azure_c_shared_utility/crt_abstractions.h>
#include <azure_c_shared_utility/platform.h>
#include <azure_c_shared_utility/shared_util_options.h>
#include <iothubtransportamqp.h>
#include <iothub.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
// Linker defined build information (see Makefile)
extern char __BUILD_DATE;
extern char __BUILD_NUMBER;
// Set the default value for module communication (e.g. AMQP) log tracing, yet
// allow compile time overrides.
#ifndef LOG_TRACE_ENABLED
#define LOG_TRACE_ENABLED 0
#endif
static void moduleTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* /*userContextCallback*/)
{
EPRINT("DEBUG: Module Twin callback called with (state=%s)", MU_ENUM_TO_STRING(DEVICE_TWIN_UPDATE_STATE, update_state));
EPRINT("DEBUG: payload=%.*s", (int)size, (const char *)payLoad);
fflush(NULL);
//JSON_Value *root_value = json_parse_string(payLoad);
//JSON_Object *root_object = json_value_get_object(root_value);
//if (json_object_dotget_value(root_object, "desired.TemperatureThreshold") != NULL) {
// temperatureThreshold = json_object_dotget_number(root_object, "desired.TemperatureThreshold");
//}
//if (json_object_get_value(root_object, "TemperatureThreshold") != NULL) {
// temperatureThreshold = json_object_get_number(root_object, "TemperatureThreshold");
//}
}
static int DirectMethodCb(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* /*userContextCallback*/)
{
const char *METHOD_NAME = "TestMethod";
const int METHOD_RESPONSE_SUCCESS = 200;
const int METHOD_RESPONSE_ERROR = 401;
int responseCode;
EPRINT("DEBUG: Method name: %s", method_name);
EPRINT("DEBUG: Method payload: %.*s", (int)size, (const char*)payload);
if (strcmp(METHOD_NAME, method_name))
{
EPRINT("Method name incorrect - expected %s but got %s", METHOD_NAME, method_name);
responseCode = METHOD_RESPONSE_ERROR;
}
/*
else if (size != strlen(expectedMethodPayload))
{
LogError("payload size incorect - expected %zu but got %zu", strlen(expectedMethodPayload), size);
responseCode = METHOD_RESPONSE_ERROR;
}
else if (memcmp(payload, expectedMethodPayload, size))
{
LogError("Payload strings do not match");
responseCode = METHOD_RESPONSE_ERROR;
}
*/
else
{
*resp_size = size;
if (size == 0)
{
*response = NULL;
EPRINT("DEBUG: Empty, but good response");
responseCode = METHOD_RESPONSE_SUCCESS;
}
else
{
if ((*response = (unsigned char*)malloc(*resp_size)) == NULL)
{
EPRINT("allocation failure");
responseCode = METHOD_RESPONSE_ERROR;
}
else
{
(void)memcpy(*response, payload, *resp_size);
EPRINT("DEBUG: All good - echoing back the payload");
responseCode = METHOD_RESPONSE_SUCCESS;
}
}
}
EPRINT("DEBUG: completing with return code %d", responseCode);
fflush(NULL);
return responseCode;
}
static void ConnectionStatusCb(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* /*userContextCallback*/)
{
EPRINT("DEBUG: ConnectionStatusCb(status=%d %s, reason=%d %s",
result, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS, result),
reason, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS_REASON, reason)
);
fflush(NULL);
}
int main(void)
{
IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle = nullptr;
int retval = 1;
do
{
printf("\n\n=======================\n");
printf("Build date : %lu\n", (unsigned long) &__BUILD_DATE);
printf("Build number: %lu\n", (unsigned long) &__BUILD_NUMBER);
fflush(NULL);
srand((unsigned int)time(NULL));
if (0 != IoTHub_Init())
{
EPRINT("Failed to initialize the platform.");
break;
}
iotHubModuleClientHandle = IoTHubModuleClient_LL_CreateFromEnvironment(AMQP_Protocol);
if (nullptr == iotHubModuleClientHandle)
{
EPRINT("IoTHubModuleClient_LL_CreateFromEnvironment failed");
break;
}
IOTHUB_CLIENT_RESULT result = IoTHubModuleClient_LL_SetModuleMethodCallback(iotHubModuleClientHandle, DirectMethodCb, iotHubModuleClientHandle);
if (IOTHUB_CLIENT_OK != result)
{
EPRINT("IoTHubModuleClient_SetModuleMethodCallback failed: %d", result);
break;
}
result = IoTHubModuleClient_LL_SetConnectionStatusCallback(iotHubModuleClientHandle, ConnectionStatusCb, iotHubModuleClientHandle);
if (IOTHUB_CLIENT_OK != result)
{
EPRINT("IoTHubDeviceClient_SetConnectionStatusCallback failed: %d", result);
break;
}
#if LOG_TRACE_ENABLED
bool traceOn = true;
IoTHubModuleClient_LL_SetOption(iotHubModuleClientHandle, OPTION_LOG_TRACE, &traceOn);
#endif // LOG_TRACE_ENABLED
result = IoTHubModuleClient_LL_SetModuleTwinCallback(iotHubModuleClientHandle, moduleTwinCallback, iotHubModuleClientHandle);
if (IOTHUB_CLIENT_OK != result)
{
EPRINT("IoTHubModuleClient_LL_SetModuleTwinCallback failed: %d", result);
break;
}
while (true)
{
IoTHubModuleClient_LL_DoWork(iotHubModuleClientHandle);
ThreadAPI_Sleep(100);
}
} while(false);
if (nullptr != iotHubModuleClientHandle)
{
IoTHubModuleClient_LL_Destroy(iotHubModuleClientHandle);
}
IoTHub_Deinit();
return retval;
}

How to use the LLVMBuildGEP function in the LLVM C API

I'm trying to make an LLVM function that reads a field from a struct i pass, I've googled some stuff about getelementptr instruction and i guess that's what I'll need here, but how is it exactly used in the C API.
The struct is defined in C but I'm trying to access its fields via LLVM since they're in the same runtime I'm guessing it will work? (Correct me if I'm wrong)
Here's the code so far.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <llvm-c/Core.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/ExecutionEngine.h>
typedef struct {
char* name;
int age;
} user;
int main() {
LLVMTypeRef type = LLVMStructCreateNamed(LLVMGetGlobalContext(), "struct.user");
LLVMModuleRef module = LLVMModuleCreateWithName("test");
LLVMBuilderRef builder = LLVMCreateBuilder();
LLVMTypeRef ret = LLVMFunctionType(LLVMPointerType(LLVMInt32Type(), 0),
(LLVMTypeRef[]){ LLVMPointerType(type, 0) }, 1, false);
LLVMValueRef fn = LLVMAddFunction(module, "stuff", ret);
LLVMBasicBlockRef entry = LLVMAppendBasicBlock(fn, "entry");
LLVMPositionBuilderAtEnd(builder, entry);
LLVMValueRef gep = LLVMBuildGEP(builder, LLVMGetParam(fn, 0), (LLVMValueRef[]){ LLVMConstInt(LLVMInt32Type(), 0, false),
LLVMConstInt(LLVMInt32Type(), 1, false) }, 2, "gep");
LLVMBuildRet(builder, gep);
char *error = NULL;
LLVMVerifyModule(module, LLVMAbortProcessAction, &error);
LLVMDisposeMessage(error);
LLVMExecutionEngineRef engine;
error = NULL;
LLVMLinkInMCJIT();
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeAsmParser();
if(LLVMCreateExecutionEngineForModule(&engine, module, &error) != 0) {
fprintf(stderr, "failed to create execution engine\n");
abort();
}
if(error) {
fprintf(stderr, "error: %s\n", error);
LLVMDisposeMessage(error);
exit(EXIT_FAILURE);
}
user m;
m.name = "John";
m.age = 17;
int* (*stuff)(user*) = (int* (*)(user*)) LLVMGetFunctionAddress(engine, "stuff");
printf("%d\n", *stuff(&m));
LLVMDisposeBuilder(builder);
LLVMDisposeExecutionEngine(engine);
return 0;
}
Basically the function I'm trying to build is equal to something like this in C
int* stuff(user* u) {
return &u->age;
}
So what happens is it crashes with a segmentation fault, i've debugged it using GDB and it seems like it happens in the line where i do the BuildGEP call with the following error from the LLVM shared library
Program received signal SIGSEGV, Segmentation fault.
0xb533baec in llvm::PointerType::get(llvm::Type*, unsigned int) ()
Now at this point i have no idea what i'm doing.
The main 2 questions i need answer for:
How do you use the LLVMBuildGEP from the C API?
Am i thinking this completely wrong?
LLVMStructCreateNamed creates an opaque struct type (like struct user; would in C), meaning LLVM doesn't know what the elements inside are. You'll need to populate that struct with a definition, likely using LLVMStructSetBody. So something like:
LLVMTypeRef struct_user_members[] = {
LLVMPointerType(LLVMInt8Type(), 0),
LLVMInt32Type()
};
LLVMStructSetBody(type, struct_user_members, 2, false);
After that, LLVMBuildGEP should be able to dereference the members, which it looks to me like you've done appropriately.

Resources