Getting the saved instruction pointer address from a signal handler - c

My question is somewhat different from others that have asked about fault addresses. I'm trying to implement a horrible hack to determine, from a signal handler, whether the signal interrupted a syscall or ordinary user code by inspecting the code at the saved instruction pointer and comparing it against the possible syscall entry instructions for the host architecture it's running on. This is part of implementing correct POSIX thread cancellation that does not suffer from the race condition and resource leak described in my old question:
How are POSIX cancellation points supposed to behave?
If this approach is unreliable or otherwise wrong, I'd also like to hear reasons.

/* sigsegv.c */
/**
* This source file is used to print out a stack-trace when your program
* segfaults. It is relatively reliable and spot-on accurate.
*
* This code is in the public domain. Use it as you see fit, some credit
* would be appreciated, but is not a prerequisite for usage. Feedback
* on it's use would encourage further development and maintenance.
*
* Due to a bug in gcc-4.x.x you currently have to compile as C++ if you want
* demangling to work.
*
* Please note that it's been ported into my ULS library, thus the check for
* HAS_ULSLIB and the use of the sigsegv_outp macro based on that define.
*
* Author: Jaco Kroon <jaco#kroon.co.za>
*
* Copyright (C) 2005 - 2010 Jaco Kroon
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
/* Bug in gcc prevents from using CPP_DEMANGLE in pure "C" */
#if !defined(__cplusplus) && !defined(NO_CPP_DEMANGLE)
#define NO_CPP_DEMANGLE
#endif
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <ucontext.h>
#include <dlfcn.h>
#ifndef NO_CPP_DEMANGLE
#include <cxxabi.h>
#ifdef __cplusplus
using __cxxabiv1::__cxa_demangle;
#endif
#endif
#ifdef HAS_ULSLIB
#include "uls/logger.h"
#define sigsegv_outp(x) sigsegv_outp(,gx)
#else
#define sigsegv_outp(x, ...) fprintf(stderr, x "\n", ##__VA_ARGS__)
#endif
#if defined(REG_RIP)
# define SIGSEGV_STACK_IA64
# define REGFORMAT "%016lx"
#elif defined(REG_EIP)
# define SIGSEGV_STACK_X86
# define REGFORMAT "%08x"
#else
# define SIGSEGV_STACK_GENERIC
# define REGFORMAT "%x"
#endif
static void signal_segv(int signum, siginfo_t* info, void*ptr) {
static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};
int i, f = 0;
ucontext_t *ucontext = (ucontext_t*)ptr;
Dl_info dlinfo;
void **bp = 0;
void *ip = 0;
sigsegv_outp("Segmentation Fault!");
sigsegv_outp("info.si_signo = %d", signum);
sigsegv_outp("info.si_errno = %d", info->si_errno);
sigsegv_outp("info.si_code = %d (%s)", info->si_code, si_codes[info->si_code]);
sigsegv_outp("info.si_addr = %p", info->si_addr);
for(i = 0; i < NGREG; i++)
sigsegv_outp("reg[%02d] = 0x" REGFORMAT, i, ucontext->uc_mcontext.gregs[i]);
#ifndef SIGSEGV_NOSTACK
#if defined(SIGSEGV_STACK_IA64) || defined(SIGSEGV_STACK_X86)
#if defined(SIGSEGV_STACK_IA64)
ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
#elif defined(SIGSEGV_STACK_X86)
ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
#endif
sigsegv_outp("Stack trace:");
while(bp && ip) {
if(!dladdr(ip, &dlinfo))
break;
const char *symname = dlinfo.dli_sname;
#ifndef NO_CPP_DEMANGLE
int status;
char * tmp = __cxa_demangle(symname, NULL, 0, &status);
if (status == 0 && tmp)
symname = tmp;
#endif
sigsegv_outp("% 2d: %p <%s+%lu> (%s)",
++f,
ip,
symname,
(unsigned long)ip - (unsigned long)dlinfo.dli_saddr,
dlinfo.dli_fname);
#ifndef NO_CPP_DEMANGLE
if (tmp)
free(tmp);
#endif
if(dlinfo.dli_sname && !strcmp(dlinfo.dli_sname, "main"))
break;
ip = bp[1];
bp = (void**)bp[0];
}
#else
sigsegv_outp("Stack trace (non-dedicated):");
sz = backtrace(bt, 20);
strings = backtrace_symbols(bt, sz);
for(i = 0; i < sz; ++i)
sigsegv_outp("%s", strings[i]);
#endif
sigsegv_outp("End of stack trace.");
#else
sigsegv_outp("Not printing stack strace.");
#endif
_exit (-1);
}
static void __attribute__((constructor)) setup_sigsegv() {
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_sigaction = signal_segv;
action.sa_flags = SA_SIGINFO;
if(sigaction(SIGSEGV, &action, NULL) < 0)
perror("sigaction");
}
$ g++ -fPIC -shared -o libsigsegv.so -ldl sigsegv
$ export LD_PRELOAD=/path/to/libsigsegv.so
I found this code on a LUG. Couldn't get to the page to point the URL here, so pasted the whole code. This code prints a small stack trace when SIGSEGV occurs. Not sure if there is some other way that does not use ucontext_t.

Related

I have a function call in one program and this function is depreciated.Is there any newer version that I can use in my code | perf_buffer__new in ebpf

I have this function which is depreciated. First how one can find the new alternative to functions that are depreciated. the function exist in libbpf library and perf_buffer__new is the exact name. so basically as the name suggest its used to create perf buffer to share info between userspace and kernel. First I like to know is perf buffers are only specific to ebpf filters or not. not means I can use perf buffers in anything. for example if I have some driver code so I just add perf buffer to have info shared between some userspace app and the driver. so some searching on the web I found it specifically link to ebpf, is this true?
So this is my code that uses call to perf_buffer__new but that function is depreciated, this function in libbpf's libbpf.h header file declarations is commented out
So I like to new what is the new alternative that I can use in my code, if there is a change in api then i like to let u know that I am trying share buffer parameter in SEC("kprobe/__x64_sys_recvfrom") to userspace for that I have used PT_REGS_PARM2 and bpf_probe_read_kernel to and included the parameter in map data. So if api is changed then how to accomplish this this is my userspace and ebpf program
Userspace.c
// SPDX-License-Identifier: GPL-2.0-only
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
#include <signal.h>
#include <bpf/libbpf.h>
//create .o file root#this:/home/ubuntu/Desktop/ebpf/kern# clang -I /lib/modules/5.14.1/build -I /usr/include/bpf/ -O2 -Wall -c trace_output_user.c
static __u64 time_get_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000ull + ts.tv_nsec;
}
static __u64 start_time;
static __u64 cnt;
#define MAX_CNT 100000ll
static void print_bpf_output(void *ctx, int cpu, void *data, __u32 size)
{
struct {
int pid;
char cookie[90];
char *buf;
} *e = data;
int i=0;
printf("hello\n");
printf(" _____________________________________________________%d \n________%s\n",e->pid,e->buf);
i++;
//printf("received map value = %s\n",e->cookie);
/*if (e->cookie != 0x12345678) {
printf("BUG pid %llx cookie %d sized %d\n",
e->pid, e->cookie, size);
return;
}
cnt++;
if (cnt == MAX_CNT) {
printf("recv %lld events per sec\n",
MAX_CNT * 1000000000ll / (time_get_ns() - start_time));
return;
}*/
}
int main(int argc, char **argv)
{
struct perf_buffer_opts pb_opts = {};
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct perf_buffer *pb;
struct bpf_object *obj;
int map_fd, ret = 0;
char filename[256];
FILE *f;
//snprintf(filename, sizeof(filename), "..o", argv[0]);
obj = bpf_object__open_file("./kprobe_send.o", NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd = bpf_object__find_map_fd_by_name(obj, "my_map");
if (map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
printf("before\n");
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
if (libbpf_get_error(prog)) {
fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
goto cleanup;
}
printf("after\n");
link = bpf_program__attach(prog);
printf("after\n");
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
printf("after\n");
pb_opts.sample_cb = print_bpf_output;
pb = perf_buffer__new_deprecated(map_fd, 8, &pb_opts);//error
printf("after\n");
ret = libbpf_get_error(pb);
if (ret) {
printf("failed to setup perf_buffer: %d\n", ret);
return 1;
}
f = popen("taskset 1 dd if=/dev/zero of=/dev/null", "r");
(void) f;
start_time = time_get_ns();
while ((ret = perf_buffer__poll(pb, 1000)) >= 0 && cnt < MAX_CNT) {
}
kill(0, SIGINT);
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return ret;
}
Kernel.c
#include <linux/ptrace.h>
#include <linux/version.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <string.h>
#include <sys/sendfile.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include </usr/include/bpf/bpf_tracing.h>
#include <linux/seccomp.h>
#define RAND_MAX 0x7fff
#define PERF_SAMPLE_RAW 1U << 0
#define randrange(N) rand() / (RAND_MAX/(N) + 1)
#define MAX 100000000 /* Values will be in the range (1 .. MAX) */
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
__uint(max_entries, 100);
} my_map SEC(".maps");
SEC("kprobe/__x64_sys_recvfrom")
int bpf_prog1(struct pt_regs *ctx)
{
static int vektor[100000000];
int candidates[MAX];
int i;
long key;
//srand(time(NULL)); /* Seed the random number generator. */
/*for (i=0; i<MAX; i++)
candidates[i] = i;
for (i = 0; i < MAX-1; i++) {
int c = randrange(MAX-i);
int t = candidates[i];
candidates[i] = candidates[i+c];
candidates[i+c] = t;
}
for (i=0; i<10; i++)
vektor[i] = candidates[i] + 1;*/
struct S {
int pid;
char cookie[90];
char *ptr;
} data={1,""};
//char *ptr = PT_REGS_PARM2(ctx);
struct seccomp_data sd;
bpf_probe_read_kernel(&sd, sizeof(sd), (void *)PT_REGS_PARM2(ctx));
if (sd.args[2] > 128 && sd.args[2] <= 1024) {
char fmt[] = "read(fd=%d, buf=%p, size=%d)\n";
bpf_trace_printk(fmt, sizeof(fmt),
sd.args[0], sd.args[1], sd.args[2]);
data.ptr=(char *)sd.args[1];
// memcpy(data.ptr,sd.args[1],sizeof(char)*220);
}
//data.pid =count;// bpf_get_current_pid_tgid();
//if(buf==NULL)
//memcpy(data.cookie,buf,20);
//data.ptr=ptr;
// data.cookie[0]=buf[0];
//bpf_get_current_comm(&data.cookie, sizeof(data.cookie));
//key=vektor[i];
//bpf_map_update_elem(fd,&key,&data,BPF_ANY);
//bpf_perf_event_output(ctx, &my_map, 1, &data, sizeof(data));
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = 99;
when I compile and link the program userspace with root#this:/home/ubuntu/Desktop/ebpf/Linux-exFilter-main/pkg/probe/bpf# clang -v trace_output_user.c -o trace -lbpf
I get error that and warning
trace_output_user.c:101:7: warning: 'perf_buffer__new_deprecated' is deprecated: libbpf v0.7+: use new variant of perf_buffer__new() instead [-Wdeprecated-declarations]
pb = perf_buffer__new_deprecated(map_fd, 8, &pb_opts);
^
/usr/include/bpf/libbpf.h:949:12: note: 'perf_buffer__new_deprecated' has been explicitly marked deprecated here
LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "use new variant of perf_buffer__new() instead")
^
/usr/include/bpf/libbpf_common.h:24:4: note: expanded from macro 'LIBBPF_DEPRECATED_SINCE'
(LIBBPF_DEPRECATED("libbpf v" # major "." # minor "+: " msg))
^
/usr/include/bpf/libbpf_common.h:19:47: note: expanded from macro 'LIBBPF_DEPRECATED'
#define LIBBPF_DEPRECATED(msg) __attribute__((deprecated(msg)))
^
1 warning generated.
"/usr/bin/ld" -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o trace /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../x86_64-linux-gnu/crt1.o /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../x86_64-linux-gnu/crti.o /usr/bin/../lib/gcc/x86_64-linux-gnu/10/crtbegin.o -L/usr/bin/../lib/gcc/x86_64-linux-gnu/10 -L/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../x86_64-linux-gnu -L/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../lib64 -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib64 -L/usr/lib/x86_64-linux-gnu/../../lib64 -L/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../.. -L/usr/lib/llvm-12/bin/../lib -L/lib -L/usr/lib /tmp/trace_output_user-ec780e.o -lbpf -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/bin/../lib/gcc/x86_64-linux-gnu/10/crtend.o /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../x86_64-linux-gnu/crtn.o
/usr/bin/ld: /tmp/trace_output_user-ec780e.o: in function `main':
trace_output_user.c:(.text+0x1e2): undefined reference to `perf_buffer__new_deprecated'
some details
perf_buffer__new_deprecated
and
perf_buffer__new are depreciated in latest version of libbpf
My kernel version is 5.14.1
1. you are explicitly using perf_buffer__new_deprecated in your code - don't do this: Use perf_buffer_new instead. You should never call a function that already has 'deprecated' in it's name.
2. Take a look in the header:
libbpf/libbpf.h
perf_buffer_new is defined like this:
#define perf_buffer__new(...) ___libbpf_overload(___perf_buffer_new, __VA_ARGS__)
#define ___perf_buffer_new6(map_fd, page_cnt, sample_cb, lost_cb, ctx, opts) \
perf_buffer__new(map_fd, page_cnt, sample_cb, lost_cb, ctx, opts)
#define ___perf_buffer_new3(map_fd, page_cnt, opts) \
perf_buffer__new_deprecated(map_fd, page_cnt, opts)
So there are 2 functions:
Old: pef_buffer_new with 3 arguments
New: perf_buffer_new with 6 arguments.
With the macros, libbpf makes old code compile, too, while telling you to change your function call.
You are using the old version right now (with 3 arguments). Switch to the new version with 6 arguments, as the 3-arguments-variant will be removed.
The new function (see libbpf/libbpf.h):
/**
* #brief **perf_buffer__new()** creates BPF perfbuf manager for a specified
* BPF_PERF_EVENT_ARRAY map
* #param map_fd FD of BPF_PERF_EVENT_ARRAY BPF map that will be used by BPF
* code to send data over to user-space
* #param page_cnt number of memory pages allocated for each per-CPU buffer
* #param sample_cb function called on each received data record
* #param lost_cb function called when record loss has occurred
* #param ctx user-provided extra context passed into *sample_cb* and *lost_cb*
* #return a new instance of struct perf_buffer on success, NULL on error with
* *errno* containing an error code
*/
LIBBPF_API struct perf_buffer *
perf_buffer__new(int map_fd, size_t page_cnt,
perf_buffer_sample_fn sample_cb, perf_buffer_lost_fn lost_cb, void *ctx,
const struct perf_buffer_opts *opts);
You can find the definitions for sample_cb and lost_cb in the header as well:
From above, we know sample_cb has the type perf_buffer_sample_fn. For the other callback, it is similar.
Both are defined in libbpf.h:
typedef void (*perf_buffer_sample_fn)(void *ctx, int cpu,
void *data, __u32 size);
typedef void (*perf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt);
See libbpf/libbpf.h
So a valid callback function could be
void myCallbackForNewData(void* ctx, int cpu, void*data, __u32 size) {}
Be aware that ctx* has nothing to do with BPF - it is something you can freely define in perf_buffer__new. This is useful if you use the same handler for multiple perf_buffers. Otherwise, you can just enter NULL.

Address boundary error when invoke swapcontext()

I've written a simple program, use ucontext library. However, a signal SIGSEGV (address boundary error) occurred. The running env is MacOS. I do not know what's wrong I made?
Updated Here: Version 2
As #Jeremy suggest, we could use static on main_context and work_context. However, if we change work_context to an array, it still failed
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/time.h>
#include <unistd.h>
#define _XOPEN_SOURCE 600
#include "ucontext.h"
static ucontext_t main_context;
static ucontext_t work_context[3]; // version 2: from ucontext_t to an array
static void counter()
{
for (int c = 0; ; c++) {
fprintf(stderr, "c = %d\n", c);
sleep(5); // avoid busy loop
}
}
static ucontext_t* make_context1(ucontext_t *ucp, void (*func)())
{
getcontext(ucp);
sigemptyset(&ucp->uc_sigmask);
void *sp = malloc(SIGSTKSZ);
if (sp == NULL) {
fprintf(stderr, "malloc failed\n");
exit(-1);
}
ucp->uc_stack = (stack_t) { .ss_sp = sp, .ss_size = SIGSTKSZ, .ss_flags = 0 };
ucp->uc_link = &main_context;
makecontext(ucp, func, 0);
return ucp;
}
int main() {
printf("start\n");
make_context1(work_context, counter);
make_context1(work_context+1, counter); // added in version 2
make_context1(work_context+2, counter); // added in version 2
swapcontext(&main_context, work_context);
printf("main exit\n");
return 0;
}
For some reason the code runs without crashing if I change these two lines:
ucontext_t main_context;
ucontext_t work_context;
to this:
static ucontext_t main_context;
static ucontext_t work_context;
I'm sure there is a good explanation for this, but I don't know what it is :(
Well, that's simple - SIGSTKSZ is too small of a stack for printf. Increase it. Quadruple it.
Move #define _XOPEN_SOURCE 600 on top of the file. See man feature_test_macros.
Add #include <signal.h> for sigemptyset. Change "ucontext.h" into <ucontext.h>- it's a standard header.

Failing to execute "make" in Ubuntu gcc

I have several files which I'm trying to compile using gcc in Ubuntu. I have vmware machine 16.04 ubuntu, which 64-bit computer. I have added the flags for 32bit compilation in the makefile. (-m32 flag).
This is the makefile:
all: clean binsem.a ut.a ph
FLAGS = -Wall -L./ -m32
ph: ph.c
gcc ${FLAGS} ph.c -lbinsem -lut -o ph
binsem.a:
gcc $(FLAGS) -c binsem.c
ar rcu libbinsem.a binsem.o
ranlib libbinsem.a
ut.a:
gcc $(FLAGS) -c ut.c
ar rcu libut.a ut.o
ranlib libut.a
clean:
rm -f *.o
rm -f a.out
rm -f *~
rm -f ph
rm -f *a
My attempts to compile the code were not successful, as I received the following error:
ph.c:126:12: warning: ‘main’ defined but not used [-Wunused-function]
/usr/lib/gcc/i686-linux-gnu/5/../../../i386-linux-gnu/crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: error: ld returned 1 exit status
Makefile:6: recipe for target 'ph' failed
make: *** [ph] Error 1
**I tried - **
Adding -c flags to the ph.c file in the make file. However it doesn't make much sense, since it has a main function. No success.
Adding sum -m32 flags in the makefile, in addition to what's already written. No Success.
The file "ph.c", is the following file -
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stdint.h>
#include <sys/time.h>
#include <inttypes.h>
#include "binsem.h"
#include "ut.h"
#define LEFT (i+N-1)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2
int N;
volatile int *phil_state;
sem_t *s;
sem_t mutex;
int *tid;
uint64_t get_wall_time() {
struct timeval time;
gettimeofday(&time, NULL);
uint64_t millis = (time.tv_sec * (uint64_t)1000) + (time.tv_usec / 1000);
return millis;
}
void think(int p) {
int i, factor;
volatile int j;
printf("Philosopher (%d) - time %" PRId64 " - is thinking\n",p, get_wall_time()); fflush (stdout);
factor = 1 + random()%5;
for (i = 0; i < 100000000*factor; i++){
j += (int) i*i;
}
printf("Philosopher (%d) - time %" PRId64 " - is hungry\n", p, get_wall_time()); fflush (stdout);
}
void eat(int p){
int i, factor;
volatile int j;
printf("Philosopher (%d) - time %" PRId64 " - is eating\n",p, get_wall_time()); fflush (stdout);
factor = 1 + random()%5;
for (i = 0; i < 100000000*factor; i++){
j += (int) i*i;
}
//printf("Philosopher (%d) - time %" PRId64 " - is thinking\n",p, get_wall_time()); fflush (stdout);
}
void test(int i){
if (phil_state[i] == HUNGRY &&
phil_state[LEFT] != EATING &&
phil_state[RIGHT] != EATING){
phil_state[i] = EATING;
binsem_up(&(s[i]));
}
}
void take_forks(int i){
binsem_down(&mutex);
phil_state[i] = HUNGRY;
test(i);
binsem_up(&mutex);
binsem_down(&(s[i]));
}
void put_forks(int i){
binsem_down(&mutex);
phil_state[i] = THINKING;
test(LEFT);
test(RIGHT);
binsem_up(&mutex);
}
void int_handler(int signo) {
long int duration;
int i;
for (i = 0; i < N; i++) {
duration = ut_get_vtime(tid[i]);
printf("Philosopher (%d) used the CPU %ld.%ld sec.\n",
i+1,duration/1000,duration%1000);
}
exit(0);
}
void philosopher(int i){
while (1){
think(i);
take_forks(i);
eat(i);
put_forks(i);
}
}
static int main(int argc, char *argv[])
{
int c;
if (argc != 2){
printf("Usage: %s N\n", argv[0]);
exit(1);
}
N = atoi(argv[1]);
if (N < 2){
printf("Usage: %s N (N >=2)\n", argv[0]);
exit(1);
}
ut_init(N);
s = (sem_t *)malloc (N * sizeof(sem_t));
phil_state = (int *) malloc (N * sizeof(int));
tid = (int *) malloc (N * sizeof(int));
for (c = 0; c < N ; c++){
phil_state[c] = THINKING;
binsem_init(&(s[c]), 0);
}
for (c = 0; c < N ; c++){
tid[c] = ut_spawn_thread(philosopher,c);
printf("Spawned thread #%d\n", tid[c]);
}
binsem_init(&mutex, 1);
signal(SIGINT,int_handler);
ut_start();
return 0; // avoid warnings
}
The makefile also tries to compile these files - ut.c, binsem.c
#include <stdlib.h>
#ifndef _UT_H
#define _UT_H
#include "ut.h"
#include <sys/time.h> // for itimerval
#include <unistd.h> // for alarm
#include <stdlib.h> // for malloc
#include <stdio.h> // for perror
#include <ucontext.h>
# include "ut.h"
#define MAX_TAB_SIZE 128 // the maximal threads table size.
#define MIN_TAB_SIZE 2 // the minimal threads table size.
#define SYS_ERR -1 // system-related failure code
#define TAB_FULL -2 // full threads table failure code
/*This type defines a single slot (entry) in the threads table. Each slot describes a single
thread. Note that we don't need to keep the thread state since every thread is always ready
or running. We also don't have to support adding/stopping thread dynamically, so we also don't
have to manage free slots.*/
typedef struct ut_slot {
ucontext_t uc;
unsigned long vtime; /* the CPU time (in milliseconds) consumed by this thread.*/
void (*func)(int); /* the function executed by the thread.*/
int arg; /* the function argument.*/
} ut_slot_t, *ut_slot;
static ut_slot threads; // pointer to thread table
static volatile int numThreads = 0; // number of threads in the table
static volatile int currentThread = 0; // current thread
static ucontext_t mainThread;
#define STACKSIZE 8192 // the thread stack size.
/* The TID (thread ID) type. TID of a thread is actually the index of the thread in the
threads table. */
typedef short int tid_t;
void handler(int signal){
alarm(1); //the alarm every second as demanded in the assignment
currentThread = (currentThread +1 ) % numThreads;
printf("in signal handler: switching from %d to %d\n", currentThread, currentThread - 1);
swapcontext(&threads[currentThread].uc, &threads[currentThread].uc); /*save current thread,
* load next thread*/
if (signal == SIGVTALRM){ // increment the time stats
threads[currentThread].vtime+=100;
} else if(signal==SIGALRM) {
if (swapcontext(&threads[currentThread - 1].uc, &threads[currentThread].uc) == -1) {
perror("could not do swapping");
exit(1);
}
}
}
int ut_init(int tab_size){
/// (###)
if(tab_size < MAX_TAB_SIZE) {
threads = (ut_slot) malloc(tab_size * sizeof(int(ut_slot_t)));
}
else{
threads = (ut_slot) malloc(MAX_TAB_SIZE * sizeof(int(ut_slot_t)));
}
if (!threads) {
return SYS_ERR;
}
return 0;
}
tid_t ut_spawn_thread(void (*func)(int), int arg){
/*uc[1].uc_link = &uc[0];
uc[1].uc_stack.ss_sp = st1; //stack fro thread 1
uc[1].uc_stack.ss_size = sizeof st1; //size of stack for therad
makecontext(&uc[1], (void(*)(void)) f, 1, 1); */
int thread_stack_size = STACKSIZE/8; //no need for 8K in size
if (numThreads>=TAB_FULL){ //(*)
return TAB_FULL;
}
if (getcontext(&threads[numThreads].uc)==-1){
return SYS_ERR;
}
ut_slot_t newThread;
threads[numThreads] = newThread;
threads[numThreads].uc.uc_link = &mainThread;
threads[numThreads].uc.uc_stack.ss_sp = (void* *)malloc(thread_stack_size);
if(threads[numThreads].uc.uc_stack.ss_sp==NULL){
return SYS_ERR;
}
makecontext(&threads[numThreads].uc,(void(*)(void))func,1,arg);
numThreads++;
return numThreads - 1;
}
int ut_start(void){
struct sigaction sigaction1;
int firstThread = 0; /*represents the current thread*/
struct itimerval itv;
/* set up vtimer for accounting */
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 10000;
itv.it_value = itv.it_interval;
/* Initialize the data structures for SIGALRM handling. */
sigaction1.sa_flags = SA_RESTART; //restart instead of throwing exception
sigfillset(&sigaction1.sa_mask); // don't throw exception for additional signals
sigaction1.sa_handler = handler; // specify handler for the sigaction declared
if (sigaction(SIGVTALRM, &sigaction1, NULL) < 0)
return SYS_ERR;
/*START THE TIMER */
if (setitimer(ITIMER_VIRTUAL, &itv, NULL) < 0)
return SYS_ERR;
if (sigaction(SIGINT, &sigaction1, NULL) < 0)
return SYS_ERR;
/* Start running. */
alarm(1); //alarm every second
if(swapcontext(&threads[firstThread].uc,&threads[firstThread+1].uc)==-1){ //swap first time
return SYS_ERR;
}
return -1;
}
unsigned long ut_get_vtime(tid_t tid){
return threads[tid].vtime;
}
#endif
binsem.c
#ifndef _BIN_SEM_H
#define _BIN_SEM_H
#include "binsem.h"
#include <signal.h>
#include "atomic.h"
typedef unsigned long sem_t;
void binsem_init(sem_t *s, int init_val){
if(init_val == 1){
*s = 1;
}
else{
*s = 0;
}
}
void binsem_up(sem_t *s){
xchg(s,1); /*send the pointer of s, and the new value 1*/
}
int binsem_down(sem_t *s){
int flag = 0;
while (flag == 0){
xchg(s,0); /*try changing the value - down*/
if (*s != 0){ /*no success in changing*/
if (raise(SIGALRM)!=0){ /*raise returns 0 on success*/
return -1;
}
} else{
flag = 1;
return 0;
}
}
}
#endif
Any ideas?
You declare the main function to be static. That means it will have internal linkage, and not be exported. Therefore the linker will not be able to find it.
Remove the static modifier and that should work.
However there are also many other problems in your Makefile that will most likely lead to problems:
The target ph only depends on ph.c, not the libraries you want to link with. You need to add the libraries as dependencies as well (their full file-names).
You should also make the libraries depend on the object files used to create them, and rely on the implicit rules to build object files from source files.
Also note that for a library named A then the file-name needs to be libA.a, otherwise the linker will not be able to find it with the -l (lower-case L) option.
The error message is pretty clear:
(.text+0x18): undefined reference to `main'
In ph.c you have (probably accustomed from Java?):
static int main(int argc, char *argv[])
In C:
static ([CPPReference]: C keywords: static) means internal linkage (only valid in current translation unit (ph.o in our case)), so a static symbol won't be "seen" by the linker (loader)
Code linked to an application (not a dynamic library or shared object (.so: linked with -shared)), must explicitly define main, and that should be visible by the linker
The combination of the above 2, lead to what you're experiencing.
Remove the static and you'll be fine.
Note: There might be other errors in the Makefile.

Compilation error using rest and rime together in Cooja

I am trying to create a ContikiOS firmware that acts as a gateway using Rime and REST. The gateway must communicate to other motes with the Rime mesh, and can communicate to the outside via a REST API.
The following code is a combination from two default Contiki examples (rest-example/rest-server-example.c and rime/example-mesh.c). Therefore I include "rest.h", "net/rime.h", "net/rime/mesh.h".
The problem is when I try to compile this firmware using following makefile, the rime files don't get included into obj_sky and any Rime functions I use in the firmware get an 'undefined reference' error. However when I remove the lines in the makefile and any code referring to REST in the firmware code, it does compile (and adds the rime files to obj_sky).
Would anyone have an idea why rime does not get added after adding the rest-http app and what I can do to have it compile?
Thank you
Code
Compiler info
> make gateway.sky TARGET=sky
CC gateway.c
CC ../../platform/sky/./contiki-sky-main.c
LD gateway.sky
/usr/lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld: gateway.sky section `.data' will not fit in region `rom'
/usr/lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld: section .vectors loaded at [000000000000ffe0,000000000000ffff] overlaps section .data loaded at [000000000000ff0c,0000000000010037]
/usr/lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld: region `rom' overflowed by 88 bytes
gateway.co: In function `process_thread_init_mesh':
gateway.c:(.text.process_thread_init_mesh+0x10): undefined reference to `mesh_close'
gateway.c:(.text.process_thread_init_mesh+0x26): undefined reference to `mesh_open'
gateway.co: In function `recv':
gateway.c:(.text.recv+0x48): undefined reference to `mesh_send'
gateway.co: In function `enable_handler':
gateway.c:(.text.enable_handler+0x80): undefined reference to `mesh_send'
collect2: ld returned 1 exit status
../../Makefile.include:254: recipe for target 'gateway.sky' failed
Process returned error code 2
make: *** [gateway.sky] Error 1
rm obj_sky/contiki-sky-main.o gateway.co
Makefile
all: gateway mote
ifndef TARGET
TARGET=sky
endif
CONTIKI=../..
WITH_UIP6=1
UIP_CONF_IPV6=1
WITH_COAP = 0
APPS += rest-http
include $(CONTIKI)/Makefile.include
Lines to compile for REST
WITH_UIP6=1
UIP_CONF_IPV6=1
WITH_COAP = 0
APPS += rest-http
gateway.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "contiki.h"
#include "contiki-net.h"
#include "rest.h"
#include "net/rime.h"
#include "net/rime/mesh.h"
#if defined (PLATFORM_HAS_LIGHT)
#include "dev/light-sensor.h"
#endif
#if defined (PLATFORM_HAS_BATT)
#include "dev/battery-sensor.h"
#endif
#if defined (PLATFORM_HAS_SHT11)
#include "dev/sht11-sensor.h"
#endif
#if defined (PLATFORM_HAS_LEDS)
#include "dev/leds.h"
#endif
#define DEBUG 1
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
#define MESSAGE "Hello"
/*---------------------------------------------------------------------------*/
/*---------------------------------RIME--------------------------------------*/
/*---------------------------------------------------------------------------*/
static struct mesh_conn mesh;
PROCESS(init_mesh, "RIME Mesh");
/*---------------------------------------------------------------------------*/
static void
sent(struct mesh_conn *c)
{
printf("packet sent\n");
}
static void
timedout(struct mesh_conn *c)
{
printf("packet timedout\n");
}
static void
recv(struct mesh_conn *c, const rimeaddr_t *from, uint8_t hops)
{
printf("Data received from %d.%d: %.*s (%d)\n",
from->u8[0], from->u8[1],
packetbuf_datalen(), (char *)packetbuf_dataptr(), packetbuf_datalen());
packetbuf_copyfrom(MESSAGE, strlen(MESSAGE));
mesh_send(&mesh, from);
}
static void
send(int to, char * msg)
{
printf("Sending data to %d", to);
rimeaddr_t addr;
packetbuf_copyfrom(msg, strlen(msg));
addr.u8[0] = to;
addr.u8[1] = 0;
mesh_send(&mesh, &addr);
}
const static struct mesh_callbacks callbacks = {recv, sent, timedout};
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(init_mesh, ev, data)
{
PROCESS_EXITHANDLER(mesh_close(&mesh);)
PROCESS_BEGIN();
mesh_open(&mesh, 132, &callbacks);
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
/*----------------------------------REST-------------------------------------*/
/*---------------------------------------------------------------------------*/
RESOURCE(enable, METHOD_GET, "enable");
void
enable_handler(REQUEST* request, RESPONSE* response)
{
int moteId = 0;
char moteIdstr[12];
sprintf(moteIdstr, "%d", moteId);
char command[20];
char responseText[255];
if (rest_get_query_variable(request, "moteId", moteIdstr, 10)) {
PRINTF("moteId %s\n", moteIdstr);
send(moteId, command);
sprintf(responseText,"Command execute for mote %s!\n", moteIdstr);
} else {
sprintf(responseText,"Mote not available!\n");
}
rest_set_header_content_type(response, TEXT_PLAIN);
rest_set_response_payload(response, (uint8_t*)responseText, strlen(responseText));
}
PROCESS(gateway_rest, "Gateway Rest");
PROCESS_THREAD(gateway_rest, ev, data)
{
PROCESS_BEGIN();
#ifdef WITH_COAP
PRINTF("COAP Server\n");
#else
PRINTF("HTTP Server\n");
#endif
rest_init();
rest_activate_resource(&resource_enable);
//rest_activate_resource(&resource_disable);
PROCESS_END();
}
/*---------------*/
AUTOSTART_PROCESSES(&init_mesh, &gateway_rest);
Info
I am using Sky motes to compile the code in Cooja.

Getting RAM size in C, linux, non-precise result

I have 16 GB RAM on my computer at school running Ubuntu 13.10. I wrote a simple C program to get the total RAM but it shows non-precise results, instead of 16 it prints out 15. How to make it to be precise, even for smaller RAM sizes? At home I have a computer with 768 MB RAM and my program shows there wrong results too :(
#include <sys/sysctl.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
void getMemoryInfo()
{
FILE *meminfo = fopen("/proc/meminfo", "r");
int totalMemory = 0;
if(meminfo == NULL)
{
exit(-1);
}
char buff[256];
while(fgets(buff, sizeof(buff), meminfo))
{
int ramKB;
if(sscanf(buff, "MemTotal: %d kB", &ramKB) == 1)
{
totalMemory = ramKB/1024.0;
totalMemory = totalMemory/1024.0;
}
}
if(fclose(meminfo) != 0)
{
exit(-1);
}
printf("%d\n", totalMemory);
}
int main()
{
getMemoryInfo();
return 0;
}
I once used this code . . . Works for most OSes
#if defined(_WIN32)
#include <Windows.h>
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#if defined(BSD)
#include <sys/sysctl.h>
#endif
#else
#error "Unable to define getMemorySize( ) for an unknown OS."
#endif
/**
* Returns the size of physical memory (RAM) in bytes.
*/
size_t getMemorySize( )
{
#if defined(_WIN32) && (defined(__CYGWIN__) || defined(__CYGWIN32__))
/* Cygwin under Windows. ------------------------------------ */
/* New 64-bit MEMORYSTATUSEX isn't available. Use old 32.bit */
MEMORYSTATUS status;
status.dwLength = sizeof(status);
GlobalMemoryStatus( &status );
return (size_t)status.dwTotalPhys;
#elif defined(_WIN32)
/* Windows. ------------------------------------------------- */
/* Use new 64-bit MEMORYSTATUSEX, not old 32-bit MEMORYSTATUS */
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
GlobalMemoryStatusEx( &status );
return (size_t)status.ullTotalPhys;
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
/* UNIX variants. ------------------------------------------- */
/* Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM */
#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_MEMSIZE)
mib[1] = HW_MEMSIZE; /* OSX. --------------------- */
#elif defined(HW_PHYSMEM64)
mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */
#endif
int64_t size = 0; /* 64-bit */
size_t len = sizeof( size );
if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
return (size_t)size;
return 0L; /* Failed? */
#elif defined(_SC_AIX_REALMEM)
/* AIX. ----------------------------------------------------- */
return (size_t)sysconf( _SC_AIX_REALMEM ) * (size_t)1024L;
#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
/* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
return (size_t)sysconf( _SC_PHYS_PAGES ) *
(size_t)sysconf( _SC_PAGESIZE );
#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE)
/* Legacy. -------------------------------------------------- */
return (size_t)sysconf( _SC_PHYS_PAGES ) *
(size_t)sysconf( _SC_PAGE_SIZE );
#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
/* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_REALMEM)
mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */
#elif defined(HW_PYSMEM)
mib[1] = HW_PHYSMEM; /* Others. ------------------ */
#endif
unsigned int size = 0; /* 32-bit */
size_t len = sizeof( size );
if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
return (size_t)size;
return 0L; /* Failed? */
#endif /* sysctl and sysconf variants */
#else
return 0L; /* Unknown OS. */
#endif
}
What you're seeing is caused by floating point inaccuracy. You must be careful when going between floating points and integers and when printing floating points.
Since floating points cannot represent all numbers exactly, it often attempts to get as close as possible. What is likely happening is that instead of 16, totalMemory is something like 15.999999999987 and then when this gets converted to an int, it gets truncated to 15.
There are two ways you can fix this: if you know totalMemory is divisible by 1024*1024, then just use integers (this would not work in the case of non integer gigabytes). Since you're using an integer anyway, you might as well use this approach. (768MB cannot be expressed as an integral amount of GB).
The other option is to add in an epsilon to prevent this. In other words, instead of using totalMemory, you would use something like totalMemory + 1e-7. The epsilon is too insignificant to make a meaningful difference, but it can push something like 15.999... up to 16.
By the way, floating point problems are only part of your problem. If you're going to use an integer, you probably want to use MB instead of GB. How can an integer represent something like 4.5GB (although that's very rare these days)?

Resources