goto, jump to entry point of mmaped ELF binary - c

i can check entry point of my binary with "$readelf cbinary -a" and through the code. But how to check its entry point virtual adr when binary is mmaped and then jump there?
int fd;
int PageSize;
char *fileName = "/home/dssiam/workspace_eclipse/hello/src/cprog";
if ((PageSize = sysconf(_SC_PAGE_SIZE)) < 0) {
perror("sysconf() Error=");
}
if ((fd = open(fileName, O_RDWR, S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
{
perror("err open file:");
exit(1);
}
else
{
fd = open(fileName, O_RDWR, S_IXUSR | S_IXGRP | S_IXOTH);
}
void *address;
int len;
off_t my_offset = 0;
len = PageSize*3; //Map one page
address = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, my_offset);
if (address == MAP_FAILED)
{
perror("mmap error. ");
}
lseek(fd, 24, SEEK_SET);
unsigned long entry_point;
read(fd, &entry_point, sizeof(entry_point)); //IT RETURN entry point adr of my binary at "/home/dssiam/workspace_eclipse/hello/src/cprog" but not in VM
printf("entry: 0x%lx\n", entry_point);
close(fd);
void *ptr = (void *)0x80484b0; // 0x80484b0 - entry_point vaddress
goto *ptr; //no jump here
so i can jump to the start of my main program, but i cant jump to the binary "cprog" stored at my hdd and mmaped region too.
any help would be appreciated.

The code has lots of mistakes (wrong mmap protection, wrong mmap start address, arbitrary pagesize, C standard specifically prohibits this kind of computed goto) but the biggest problem is that this method simply will not work, except maybe for the most basic cases.
You cannot just mmap a single function from elf file into the memory and expect it to work -- you will need to perform relocations for relocatable code, and even for PIC (position independent code), you still need to create GOT.
I am going to guess that what you really want to dynamically load complied files, so use a standard way to do this: compile your file into .so dynamic library, then use dlopen/dlsym to access functions from the file.

Related

mmap memory backed by other memory?

I'm not sure if this question makes sense, but let's say I have a pointer to some memory:
char *mem;
size_t len;
Is it possible to somehow map the contents of mem to another address as a read-only mapping? i.e. I want to obtain a pointer mem2 such that mem2 != mem and accessing mem2[i] actually reads mem[i] (without doing a copy).
My ultimate goal would be to take non-contiguous chunks of memory and make them appear to be contiguous by mapping them next to each other.
One approach I considered is to use fmemopen and then mmap, but there's no file descriptor associated with the result of fmemopen.
General case - no control over first mapping
/proc/[PID]/pagemap + /dev/mem
The only way I can think of making this work without any copying is by manually opening and checking /proc/[PID]/pagemap to get the Page Frame Number of the physical page corresponding to the page you want to "alias", and then opening and mapping /dev/mem at the corresponding offset. While this would work in theory, it would require root privileges, and is most likely not possible on any reasonable Linux distribution since the kernel is usually configured with CONFIG_STRICT_DEVMEM=y which puts strict restrictions over the usage of /dev/mem. For example on x86 it disallows reading RAM from /dev/mem (only allows reading memory-mapped PCI regions). Note that in order for this to work the page you want to "alias" needs to be locked to keep it in RAM.
In any case, here's an example of how this would work if you were able/willing to do this (I am assuming x86 64bit here):
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
/* Get the physical address of an existing virtual memory page and map it. */
int main(void) {
FILE *fp;
char *endp;
unsigned long addr, info, physaddr, val;
long off;
int fd;
void *mem;
void *orig_mem;
// Suppose that this is the existing page you want to "alias"
orig_mem = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (orig_mem == MAP_FAILED) {
perror("mmap orig_mem failed");
return 1;
}
// Write a dummy value just for testing
*(unsigned long *)orig_mem = 0x1122334455667788UL;
// Lock the page to prevent it from being swapped out
if (mlock(orig_mem, 0x1000)) {
perror("mlock orig_mem failed");
return 1;
}
fp = fopen("/proc/self/pagemap", "rb");
if (!fp) {
perror("Failed to open \"/proc/self/pagemap\"");
return 1;
}
addr = (unsigned long)orig_mem;
off = addr / 0x1000 * 8;
if (fseek(fp, off, SEEK_SET)) {
perror("fseek failed");
return 1;
}
// Get its information from /proc/self/pagemap
if (fread(&info, sizeof(info), 1, fp) != 1) {
perror("fread failed");
return 1;
}
physaddr = (info & ((1UL << 55) - 1)) << 12;
printf("Value: %016lx\n", info);
printf("Physical address: 0x%016lx\n", physaddr);
// Ensure page is in RAM, should be true since it was mlock'd
if (!(info & (1UL << 63))) {
fputs("Page is not in RAM? Strange! Aborting.\n", stderr);
return 1;
}
fd = open("/dev/mem", O_RDONLY);
if (fd == -1) {
perror("open(\"/dev/mem\") failed");
return 1;
}
mem = mmap(NULL, 0x1000, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, fd, physaddr);
if (mem == MAP_FAILED) {
perror("Failed to mmap \"/dev/mem\"");
return 1;
}
// Now `mem` is effecively referring to the same physical page that
// `orig_mem` refers to.
// Try reading 8 bytes (note: this will just return 0 if
// CONFIG_STRICT_DEVMEM=y).
val = *(unsigned long *)mem;
printf("Read 8 bytes at physaddr 0x%016lx: %016lx\n", physaddr, val);
return 0;
}
userfaultfd(2)
Other than what I described above, AFAIK there isn't a way to do what you want from userspace without copying. I.E. there is not a way to simply tell the kernel "map this second virtual addresses to the same memory of an existing one". You can however register an userspace handler for page faults through the userfaultfd(2) syscall and ioctl_userfaultfd(2), and I think this is overall your best shot.
The whole mechanism is similar to what the kernel would do with a real memory page, only that the faults are handled by a user-defined userspace handler thread. This is still pretty much an actual copy, but is atomic to the faulting thread and gives you more control. It could potentially also perform better in general since the copying is controlled by you and can therefore be done only if/when needed (i.e. at the first read fault), while in the case of a normal mmap + copy you always do the copying regardless if the page will ever be accessed later or not.
There is a pretty good example program in the manual page for userfaultfd(2) which I linked above, so I'm not going to copy-paste it here. It deals with one or more pages and should give you an idea about the whole API.
Simpler case - control over the first mapping
In the case you do have control over the first mapping which you want to "alias", then you can simply create a shared mapping. What you are looking for is memfd_create(2). You can use it to create an anonymous file which can then be mmaped multiple times with different permissions.
Here's a simple example:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
int main(void) {
int memfd;
void *mem_ro, *mem_rw;
// Create a memfd
memfd = memfd_create("something", 0);
if (memfd == -1) {
perror("memfd_create failed");
return 1;
}
// Give the file a size, otherwise reading/writing will fail
if (ftruncate(memfd, 0x1000) == -1) {
perror("ftruncate failed");
return 1;
}
// Map the fd as read only and private
mem_ro = mmap(NULL, 0x1000, PROT_READ, MAP_PRIVATE, memfd, 0);
if (mem_ro == MAP_FAILED) {
perror("mmap failed");
return 1;
}
// Map the fd as read/write and shared (shared is needed if we want
// write operations to be propagated to the other mappings)
mem_rw = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, 0);
if (mem_rw == MAP_FAILED) {
perror("mmap failed");
return 1;
}
printf("ro mapping # %p\n", mem_ro);
printf("rw mapping # %p\n", mem_rw);
// This write can now be read from both mem_ro and mem_rw
*(char *)mem_rw = 123;
// Test reading
printf("read from ro mapping: %d\n", *(char *)mem_ro);
printf("read from rw mapping: %d\n", *(char *)mem_rw);
return 0;
}

Attaching to different parts of shared memory not working properly

I am using shared memory for communication between two different process. I am creating shared memory of 16 MB size. I am trying to attach two different parts of the shared memory. One for writing and other for reading. Even though it maps to different memory address but when one is modified other also gets changed. I must be doing something wrong. Below is the code snippet where I am attaching to multiple shared memory location.
void createCommPool ()
{
CommSet set1;
int shmid1;
int fd1;
int r;
void * ptr;
void * ptr_res;
umask (0);
fd1 = open(SHARED_MEMORY0, O_CREAT | O_TRUNC | O_RDWR, 0777);
if (fd1 == -1)
error_and_die("open");
r = ftruncate(fd1, region_size);
if (r != 0)
error_and_die("ftruncate");
ptr = mmap(0, sizeof(struct operation_st), PROT_READ | PROT_WRITE,
,MAP_SHARED,fd1,sizeof(struct operation_st));
if (ptr == MAP_FAILED)
error_and_die("mmap");
close(fd1);
set1.shm_addr = ptr;
fd1 = open(SHARED_MEMORY0, O_RDWR, 0777);
if (fd1 == -1)
error_and_die("open");
fprintf(stderr,"The value of the file descriptor:%d\n",fd1);
if (lseek(fd1,sizeof(struct operation_st),SEEK_SET)<0)
{
fprintf(stderr,"could not perform lseek\n");
perror("lseek");
}
ptr_res = mmap(0,sizeof(struct operation_st), PROT_READ| PROT_WRITE,
MAP_SHARED,fd1,0);
if (ptr_res == MAP_FAILED)
error_and_die("mmap2");
close(fd1);
set1.shm_addr_res = ptr_res;
}
For data in shared memory, avoid the influence of bytes alignment with pack:
#pragma pack(1)
your shared memory code
#pragma unpack
lseek does not have any effect on the mapping of the shared memory. The offset parameter should be used in order to map to the different part of the shared memory. The offset should be in multiples of page size.

Bus error (core dumped) when using strcpy to a mmap'ed file

I have a simple program going this:
int main(void) {
int fd;
const char *text = "This is a test";
fd = open("/tmp/msyncTest", (O_CREAT | O_TRUNC | O_RDWR), (S_IRWXU | S_IRWXG | S_IRWXO) );
if ( fd < 0 ) {
perror("open() error");
return fd;
}
/* mmap the file. */
void *address;
off_t my_offset = 0;
address = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, fd, my_offset);
if ( address == MAP_FAILED ) {
perror("mmap error. " );
return -1;
}
/* Move some data into the file using memory map. */
strcpy( (char *)address, text);
/* use msync to write changes to disk. */
if ( msync( address, 4096 , MS_SYNC ) < 0 ) {
perror("msync failed with error:");
return -1;
}
else {
printf("%s","msync completed successfully.");
}
close(fd);
unlink("/tmp/msyncTest");
}
Anything wrong with my code? I have made some simple tests and it seems that the problem comes from strcpy. But according to the definition, I see no problem.
If
fd = open("/tmp/msyncTest", (O_CREAT | O_TRUNC | O_RDWR), (S_IRWXU | S_IRWXG | S_IRWXO) );
is successful, fd will refer to a zero-length file (O_TRUNC). The call to mmap()
address = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, fd, my_offset);
establishes a memory-mapping, but the pages do not correspond to an object.
http://pubs.opengroup.org/onlinepubs/7908799/xsh/mmap.html has the following to say about this situation:
The system always zero-fills any partial page at the end of an object. Further, the system never writes out any modified portions of the last page of an object that are beyond its end. References within the address range starting at pa and continuing for len bytes to whole pages following the end of an object result in delivery of a SIGBUS signal.
Similarly, man mmap on Linux notes
Use of a mapped region can result in these signals:[...] SIGBUS Attempted access to a portion of the buffer that does not correspond to the file (for example, beyond the end of the file, including the case where another process has truncated the file).
Consequently, you must ftruncate() the file to a non-zero length before mmap()ing it (unless you are mmap()ing anonymous memory).

Lifetime of MMAP value in linux

Hi im using a beaglebone black running on debian, and i use mmap on /dev/mem file to access GPIO registers.
I have a .c file that contains my mapping function:
//sample code
unsigned int *gpio_get_map(int gpio)
{
unsigned int *gpio_addr = NULL;
int fd = open("/dev/mem", O_RDWR);
gpio_addr = mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE,MAP_SHARED,fd, gpio_get_number(gpio));
if(gpio_addr == MAP_FAILED)
{
printf("Unable to map GPIO : %s\n",strerror(errno));
close(fd);
return NULL;
}
close(fd);
return gpio_addr;
}
Then i call this function in another .c file to get the value of gpio_addr and use it to manipulate the GPIOs, it works fine but i am not sure how long the gpio_addr will be valid.
Will the address given by the gpio_addr be always valid? Or should i call another mmap after some period of time? Thanks.
You don't need to renew the mapping. It will stay valid in the calling process until you explicitly unmap it (or the process ends).

how to make a process shared memory (c, linux)?

I have a process that dived itself with fork. I need to create a region of memory (a matrix) for the result of the computation of each process. How can I do this? Everything I tried or I can use but it's not shared between processes or I can't use (not sure if shared or not). Someone knows what I can use? It can be something simple and without any security. The simpler the better.
I tried shmget but it's not sharing and I couldn't get how to use mmap to allocate or use it correctly. I tried other estranges things, but nothing. Any tips?
Some tries:
segment_id = shmget(IPC_PRIVATE, (sizeof(int) * linhas_mat1 * colunas_mat2) , S_IRUSR|S_IWUSR);
matriz_result = (int **) shmat(segment_id, NULL, 0);
Forks after that. Each process can use the matriz_result normally as a matrix, but the memory is not shared. Each one has one like a local variable.
segment_id = shm_open("/myregion", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
matriz_result = mmap(NULL, (sizeof(int) * linhas_mat1 * colunas_mat2), PROT_READ | PROT_WRITE, MAP_SHARED, segment_id, 0);
Tried this with mmap, but I don't know if it's right. I'm not good with such low level programming and I couldn't find any good example on how to use it correctly.
declarations:
int segment_id is;
int **matriz_result;
int createMemShare(){
//File descriptor declaration:
int fd;
//We want to open the file with readwrite,create it, and empty it if it exists
//We want the user to have permission to read and write from it
fd = open(MEMSHARENAME, O_RDWR| O_CREAT | O_TRUNC, S_IRUSR| S_IWUSR );
if(fd <= 0){
puts("Failed in creating memory share .");
return -1;
}
//Move the file pointer and write an empty byte, this forces the file to
//be of the size we want it to be.
if (lseek(fd, MEMSHARESIZE - 1, SEEK_SET) == -1) {
puts("Failed to expand the memory share to the correct size.");
return -1;
}
//Write out 1 byte as said in previous comment
write(fd, "", 1);
//Memory share is now set to use, send it back.
return fd;
}
//Later on...
int memShareFD = mmap(NULL, MEMSHARESIZE, PROT_READ, MAP_SHARED, fd, 0);
//And to sync up data between the processes using it:
//The 0 will invalidate all memory so everything will be checked
msync(memshareFD,0,MS_SYNC|MS_INVALIDATE);
you can try the above function to create a shared memory space. Essentially all you need to do is treat it like any other file once you've made it. The code example on the man page is pretty complete and worth a look into: check it out here
Edit:
You'd probably be better off using shm_open as Jens Gustedt suggested in the comments. It's simple to use and simpler than making the file yourself with the function I've written above.

Resources