This is my first post and I am trying to adhere to the forum rules.
I know this question has been asked a number of times before, but the provided solutions do not work for me. I am not quite sure why this is so, probably I have overlooked something.
I want to access from a shared module (.so) symbols (variables and functions) which reside inside the main application.
I am always getting link errors ("undefined reference").
The scenario is similar as described here, with the one expection that I am using plain C instead of C++:
shared object can't find symbols in main binary, C++
Maybe this difference is the reason for that the proposed solution does not work.
I am using 32bit MinGW, version 4.6.2 under 32bit Windows (XP if that matters).
I also read these posts:
Receive "undefined symbol" error when loading library with dlopen
Dynamic loading and symbols sharing
Can I access to symbols of the host proccess from a shared object loaded in runtime? Any alternative?
The latter solution would work, I suppose, but is not an option because this is a porting project from Linux with 100+ symbols. It is a plugin system and I do not want to limit the possibilities avalable for the plugins.
I tried a number of commandline switches when linking the main application, such as -rdynamic (is apparently not supported in MinGW), --export-all-symbols, --export-dynamic.
I also tried different commandline options when linking the shared library, such as --allow-shlib-undefined, --enable-auto-import, --enable-runtime-pseudo-reloc
The application is a linux application and works well there; I managed it to make it work under Mac OSX (Darwin) as well. There I must not strip the main application executable during linking and specify "-bundle -bundle_loader 'app name'" when linking the shared objects.
Many thanks in advance!
Johannes
Well, I found out myself with some help of the codeguru forum (see http://forums.codeguru.com/showthread.php?536343-RESOLVED-accessing-symbols-in-appllication-from-within-dynamic-library-MinGW ).
The solution is not - as I was thinking first - some option to the linker; apparently automatic back-linking is not possible in Windows. You have to manully load the symbols you want to use from the main application at runtime; similiar to when you manually load a DLL.
I made a small test and demonstration project:
file "my_app.c":
#include <stdio.h>
#include <stdlib.h>
#include "my_app.h"
#include "plugins.h"
int a;
int b;
Plugin *P;
void hello ()
{
printf ("Hello World!\n");
}
int main ()
{
char choice;
char buf[BUFLEN];
char *plugin_name=NULL;
Plugin *p=NULL;
do
{
printf ("\n --- Menu items ---\n");
printf (" '1' .. select plugin\n");
printf (" '2' .. load plugin\n");
printf (" '3' .. execute plugin function\n");
printf (" '4' .. unload plugin\n");
printf (" 'x' .. exit\n");
printf ("\n Your choice: ");
fgets (buf, sizeof(buf), stdin);
putchar ('\n');
choice = buf[0];
switch (choice)
{
case '1':
plugin_name = plugin_select ();
break;
case '2':
if (plugin_name)
{
p = plugin_load (plugin_name);
if (p)
printf ("Plugin '%s' loaded successfully\n", plugin_name);
P = p;
} else
printf ("No plugin selected\n");
break;
case '3':
if (p)
{
printf ("Enter the 1st number: ");
fgets (buf, sizeof(buf), stdin);
a = atoi (buf);
printf ("Enter the 2nd number: ");
fgets (buf, sizeof(buf), stdin);
b = atoi (buf);
printf ("\nExecuting the plugin '%s' ...\n", plugin_name);
plugin_action (p);
} else
printf ("No plugin loaded\n");
break;
case '4':
if (p)
{
plugin_destroy (p);
p = NULL;
P = NULL;
} else
printf ("No plugin loaded\n");
break;
}
} while (choice != 'x');
return 0;
}
file "my_app.h":
#ifndef __MYAPP_H
#define __MYAPP_H
#include "plugins.h"
#define TRUE 1
#define FALSE 0
#define BUFLEN 120
extern int a;
extern int b;
extern void hello (void);
extern Plugin *P;
#endif /* __MYAPP_H */
file "plugins.c":
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <dlfcn.h>
#include "my_app.h"
#include "plugins.h"
Plugin *plugin_load (const char *filename)
{
Plugin *p;
int (*init)(Plugin *);
p = malloc (sizeof(Plugin));
if (p == NULL)
{
printf ("Couldn't allocate Plugin object\n");
return NULL;
}
strncpy (p->filename, filename, sizeof(p->filename));
if ((p->module = dlopen(p->filename, RTLD_LAZY)) == NULL)
{
printf ("%s\n", dlerror());
free (p);
return NULL;
}
init = dlsym (p->module, "init_plugin");
if (init == NULL)
{
printf ("%s\n", dlerror());
free (p);
return NULL;
}
if (!init(p))
{
perror ("Couldn't initialize plugin");
free (p);
return NULL;
}
p->load = TRUE;
return p;
}
void plugin_action (Plugin *p)
{
if (p == NULL)
{
printf ("Plugin not found!\n");
return;
}
p->plugin_cb();
}
char *plugin_select ()
{
DIR *dp;
struct dirent *e;
static char buf[BUFLEN];
char *p;
size_t len;
char *suffix;
dp = opendir (".");
if (dp == NULL)
{
perror ("Cannot open '.'");
return NULL;
}
printf ("\nFollowing plugins are available:\n");
while ((e=readdir(dp)) != NULL)
{
suffix = strrchr (e->d_name, '.');
if (suffix)
{
if (strcmp (suffix, PLUGIN_SUFFIX) == 0)
printf (" %s\n", e->d_name);
}
}
closedir (dp);
printf ("Your choice: ");
buf[0] = '.';
buf[1] = '/';
p = &buf[2];
fgets (p, sizeof(buf)-2, stdin);
len = strlen (buf);
if (len > 0)
len --;
buf[len] = '\0';
if (strchr(p, '.') == NULL)
strcat (buf, PLUGIN_SUFFIX);
return buf;
}
void plugin_destroy (Plugin *p)
{
void (*unload)(Plugin *);
unload = dlsym (p->module, "unload_plugin");
if (unload != NULL)
unload (p);
dlclose (p->module);
free (p);
}
file "plugins.h":
#ifndef __PLUGINS_H
#define __PLUGINS_H
#include <limits.h> /*PATH_MAX */
#define PLUGIN_SUFFIX ".so"
typedef struct _Plugin
{
char filename[PATH_MAX];
char *name;
char *description;
unsigned int show_in_menu;
unsigned int load;
void *module;
void (*plugin_cb)();
} Plugin;
Plugin *plugin_load (const char *filename);
void plugin_action (Plugin *p);
char *plugin_select (void);
void plugin_destroy (Plugin *p);
#endif /* __PLUGINS_H */
Now the plugins:
Hide the nasty function pointer declarations, etc.: "plugin_macros.h":
#ifndef __PLUGIN_MACROS_H
#define __PLUGIN_MACROS_H
#ifdef WIN32
#include <windows.h>
#define DECLARE_FUNC(type,name,par) typedef type (WINAPI *name ## _t)par; static name ## _t name;
#define DECLARE_VAR(type,name) static type * p ## name;
#define DECLARE_PTR(type,name) static type *name;
#define LOAD_FUNCC(name) if((name=(name ## _t)GetProcAddress(GetModuleHandle(NULL),#name))==NULL) return 0
#define LOAD_FUNC(name) (name=(name ## _t)GetProcAddress(GetModuleHandle(NULL),#name))
#define LOAD_VARC(type,name) if((p ## name=(type *)GetProcAddress(GetModuleHandle(NULL),#name))==NULL) return 0
#define LOAD_VAR(type,name) (name=(type *)GetProcAddress(GetModuleHandle(NULL),#name))
#define LOAD_PTRC(type,name) if((name=*(type **)GetProcAddress(GetModuleHandle(NULL),#name))==NULL) return 0
#define LOAD_PTR(type,name) (name=*(type **)GetProcAddress(GetModuleHandle(NULL),#name))
#else
/* not WIN32 */
#define DECLARE_FUNC(type,name,par)
#define DECLARE_VAR(type,name)
#define DECLARE_PTR(type,name)
#define LOAD_FUNCC(name)
#define LOAD_FUNC(name)
#define LOAD_VARC(type,name)
#define LOAD_VAR(type,name)
#define LOAD_PTRC(type,name)
#define LOAD_PTR(type,name)
#endif
#endif /* __PLUGIN_MACROS_H */
file "add.c":
#include <stdio.h>
#include "plugins.h"
#include "plugin_macros.h"
#ifdef WIN32
#include <windows.h>
DECLARE_FUNC(void, hello, (void));
DECLARE_VAR(int, a);
DECLARE_VAR(int, b);
DECLARE_PTR(Plugin, P);
#define a (*pa)
#define b (*pb)
#else
#include "my_app.h"
#endif
static void add ()
{
hello();
LOAD_PTR(Plugin, P);
printf ("name=%s\n", P->name);
printf ("description=%s\n\n", P->description);
printf ("%d + %d = %d\n", a, b, a+b);
}
int init_plugin (Plugin *p)
{
LOAD_FUNCC(hello);
LOAD_VARC(int, a);
LOAD_VARC(int, b);
p->name = "Add";
p->description = "Add two integers.";
p->plugin_cb = add;
P = p;
return TRUE;
}
file "multiply.c":
#include <stdio.h>
#include "plugins.h"
#include "plugin_macros.h"
#ifdef WIN32
#include <windows.h>
DECLARE_VAR(int, a);
DECLARE_VAR(int, b);
DECLARE_PTR(Plugin, P);
#define a (*pa)
#define b (*pb)
#else
#include "my_app.h"
#endif
static void multiply ()
{
LOAD_PTR(Plugin, P);
printf ("name=%s\n", P->name);
printf ("description=%s\n\n", P->description);
printf ("%d * %d = %d\n", a, b, a*b);
}
int init_plugin (Plugin *p)
{
LOAD_VARC(int, a);
LOAD_VARC(int, b);
p->name = "Multiply";
p->description = "Multiply two integers.";
p->plugin_cb = multiply;
P = p;
return TRUE;
}
... and the Makefile:
ifeq ($(OS), Windows_NT)
TARGET = plugintest.exe
CC = $(MINGW)\bin\gcc.exe
LDFLAGS = -Wl,--export-all-symbols
STRIP = -s
SHARED = -shared
RM = del
else
TARGET = plugintest
UNAME := $(shell uname -s)
ifeq ($(UNAME),Linux)
LDFLAGS = -Wl,--export-dynamic
STRIP = -s
SHARED = -shared
endif
ifeq ($(UNAME),Darwin)
LDFLAGS =
STRIP =
SHARED = -bundle -bundle_loader plugintest
endif
RM = rm -f
endif
PLUGINS = add.so multiply.so
OBJS = my_app.o plugins.o
LIBS = -ldl
CFLAGS = -Wall
all: $(TARGET) my_plugins
$(TARGET): $(OBJS)
$(CC) $(OBJS) $(LIBS) $(LDFLAGS) -o $(TARGET)
my_app.o: my_app.c my_app.h plugins.h
$(CC) $(CFLAGS) -c my_app.c -o my_app.o
plugins.o: plugins.c my_app.h plugins.h
$(CC) $(CFLAGS) -c plugins.c -o plugins.o
my_plugins: $(PLUGINS)
add.so: add.c my_app.h plugin_macros.h
$(CC) $(CFLAGS) $(SHARED) add.c -o add.so
multiply.so: multiply.c my_app.h plugin_macros.h
$(CC) $(CFLAGS) $(SHARED) multiply.c -o multiply.so
clean:
$(RM) *.o *.so $(TARGET)
The project uses the posix dynamic loading functions dlopen(), etc. They are not part of the MinGW installation, but you can download from here:
http://code.google.com/p/dlfcn-win32/
I hope this helps others who come across the same issue.
Any comments and questions are welcome!
Johannes
Related
I tried to write a self-modifying code. (Refer to the link https://shanetully.com/2013/12/writing-a-self-mutating-x86_64-c-program/) The self-modifying code works when there is no optimization (-o0)
gcc -O0 smc.c -o smc
Calling foo...
i: 1
Calling foo...
i: 42
while when the optimization level increases (-O1-O2-O3..) Self-modifying code no longer works.
gcc -O3 smc.c -o smc
Calling foo...
i: 1
Calling foo...
i: 1
Is it possible to make self-modifying code work with -O3 level optimization, and what should I do?
The program is as follows:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
void foo(void);
int change_page_permissions_of_address(void *addr);
int main(void) {
void *foo_addr = (void*)foo;
// Change the permissions of the page that contains foo() to read, write, and execute
// This assumes that foo() is fully contained by a single page
if(change_page_permissions_of_address(foo_addr) == -1) {
fprintf(stderr, "Error while changing page permissions of foo(): %s\n", strerror(errno));
return 1;
}
// Call the unmodified foo()
puts("Calling foo...");
foo();
// Change the immediate value in the addl instruction in foo() to 42
unsigned char *instruction = (unsigned char*)foo_addr + 22;
// Notice that 22 here is the offset that I compiled. Different compilations and machine offsets may vary.
*instruction = 0x2A;
// Call the modified foo()
puts("Calling foo...");
foo();
return 0;
}
void foo(void) {
int i=0;
i++;
printf("i: %d\n", i);
}
int change_page_permissions_of_address(void *addr) {
// Move the pointer to the page boundary
int page_size = getpagesize();
addr -= (unsigned long)addr % page_size;
if(mprotect(addr, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
{
return -1;
}
return 0;
}
I just make a mistake about the macro LINUX_VERSION_CODE.
Here is what I want to do,when execute the code,the code run different branch due to the machine ,which is running.
I just realized,the result is not change,since I compile my code always in the same server.For example I compile the belw code in kernel version of 2.6.18,then the LINUX_VERSION_CODE marco is always 132626 .
If there is any way ,let the run different branch due to the version of which it runs?
#include <stdio.h>
#include <linux/version.h>
int main()
{
printf("kernel version(2.6.32) = %d\n",KERNEL_VERSION(2,6,32));
printf("LINUX_VERSION_CODE = %d\n",LINUX_VERSION_CODE);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
printf("Hello,world.\n");
#else
printf("Fine,thank you.\n");
#endif
return 0;
}
So you want to run a different code depending on kernel version. But you don't want to decide on that using a compile time constant - you want that at runtime.
Nothing simpler, but a call to uname:
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
struct utsname name;
if (uname(&name)) {
fprintf(stderr, "Uname failed!\n");
exit(-1);
}
printf("%s\n", name.release);
if (!memcmp(&name.release, "4.18.9-", sizeof("4.18.9-") - 1)) {
printf("Och, kernel 4.18.9 found!\n");
} else {
printf("Och, you've got a different kernel...\n");
}
}
On my machine:
$ cat 1.c | gcc -xc - && ./a.out
4.18.9-arch1-1-ARCH
Och, kernel 4.18.9 found!
On my friends machine:
cat 1.c | ssh friend 'gcc -xc - && ./a.out'
4.12.14-lp150.11-default
Och, you've got a different kernel...
I will leave it to the OP, to call strtoll or sscanf(&name.release, "%d.%d.%d-", &major, &minor, &release) to get the kernel version as integer number.
But you can get way more hardcore than that. On runtime, you can just do anything, so just read the content of /usr/include/linux/version.h file:
#define _GNU_SOURCE 1
#include <linux/version.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE *f;
f = fopen("/usr/include/linux/version.h", "r");
if (f == NULL) return -__LINE__;
char *line = NULL;
size_t linelen = 0;
char *found = NULL;
while (getline(&line, &linelen, f) > 0) {
if ((found = strstr(line, "LINUX_VERSION_CODE")) != NULL) {
break;
}
}
if (found == NULL) return -__LINE__;
fclose(f);
found += sizeof("LINUX_VERSION_CODE") - 1;
errno = 0;
const long long kv = strtoll(found, NULL, 10);
if (errno) return -__LINE__;
free(line);
printf("%ld.%ld.%ld\n", kv>>16&0xff, kv>>8&0xff, kv&0xff);
if (kv > KERNEL_VERSION(4,17,0)) {
printf("Och, kernel api greater then 4.17.0 found!\n");
} else {
printf("Och, kernel api below 4.17.0 found!\n");
}
}
And on my machine this outputs:
$ cat 1.c | gcc -xc - && ./a.out
4.17.11
Och, kernel api greater then 4.17.0 found!
And on my friends:
$ cat 1.c | ssh friend 'gcc -xc - && ./a.out'
4.15.0
Och, kernel api below 4.17.0 found!
We can also see, that uname -a != grep "LINUX_VERSION_CODE" /usr/include/linux/version.h.
I tried to make a simple kernel module invalidating all tlb entries through user-level ioctl.
Below is my example code [flush_tlb.c]
/*
* flush_tlb.c
*/
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
#include "flush_tlb.h"
MODULE_LICENSE("GPL");
static long flush_tlb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct flush_tlb_parv_t flush_tlb_parv;
// copy an argument from user-level
if (size != sizeof(struct flush_tlb_parv_t)) {
ret = -EINVAL;
goto out;
}
if (copy_from_user(&flush_tlb_parv, ubuf, sizeof(flush_tlb_parv))) {
ret = -EFAULT;
goto out;
}
if (flush_tlb_parv.addr) {
// Not Implemented
flush_tlb_all();
} else {
flush_tlb_all();
}
out:
return ret;
}
static const struct file_operations flush_tlb_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = flush_tlb_ioctl,
.compat_ioctl = flush_tlb_ioctl,
};
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "flush_tlb",
.fops = &flush_tlb_fops
};
static int __init flush_tlb_init(void)
{
int ret;
ret = misc_register(&binder_miscdev);
return ret;
}
module_init(flush_tlb_init)
static void __exit flush_tlb_exit(void)
{
misc_deregister(&binder_miscdev);
}
module_exit(flush_tlb_exit)
And here is my [Makefile]
obj-m += flush_tlb.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
It is successfully compiled, but when I insert the module by insmod, it fails with following kernel logs.
"flush_tlb: Unknown symbol flush_tlb_all (err 0)"
Also, I checked the /proc/kallsyms that flush_tlb_all exists in the symbol table.
$cat /proc/kallsyms | grep flush_tlb_all
ffffffff81022f90 T xen_flush_tlb_all
ffffffff810274a0 t trace_raw_output_xen_mmu_flush_tlb_all
ffffffff81027af0 t trace_event_raw_event_xen_mmu_flush_tlb_all
ffffffff81028c20 t perf_trace_xen_mmu_flush_tlb_all
ffffffff81072670 t do_flush_tlb_all
ffffffff81072bf0 T flush_tlb_all
ffffffff810792c0 t __flush_tlb_all
ffffffff8107d7cf t __flush_tlb_all
ffffffff81d76570 r __tracepoint_ptr_xen_mmu_flush_tlb_all
ffffffff81d77630 r __tpstrtab_xen_mmu_flush_tlb_all
ffffffff81e231f8 d print_fmt_xen_mmu_flush_tlb_all
ffffffff81e23f20 d trace_event_type_funcs_xen_mmu_flush_tlb_all
ffffffff81e24780 d event_xen_mmu_flush_tlb_all
ffffffff81f04720 d event_class_xen_mmu_flush_tlb_all
ffffffff81f103a0 D __tracepoint_xen_mmu_flush_tlb_all
ffffffff81f6772c t trace_event_define_fields_xen_mmu_flush_tlb_all
ffffffff8208c080 t __event_xen_mmu_flush_tlb_all
How can I fix it?
Thanks.
I have a stripped binary and symbol-file. Is it possible to add the symbols back to binary and create an unstripped binary.
My use-case is using this binary w/ valgrind.
For those tools that do not support separate files for debug information, you can glue the debug sections back to the original binary.
You can do something along these lines, for example:
First build a small program that efficiently extracts an arbitrary chunk from a file
(note that dd will not do this efficiently as we'd have to use bs=1 to support an arbitrary offset and length, and objcopy -O binary does not copy sections that are not ALLOC, LOAD※)
cat <<EOF | gcc -xc -o ./mydd -
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <macros.h>
char buf[1024*1024];
int main(int argc, char** argv) {
char *fin, *fout;
int fdin, fdout;
off_t off;
size_t len;
ssize_t rd;
int status;
if (argc != 5) {
fprintf(stderr, "Usage: %s fin skip count fout\n", argv[0]);
return 1;
}
fin = argv[1];
off = strtoul(argv[2], NULL, 0);
len = strtoul(argv[3], NULL, 0);
fout = argv[4];
fdin = -1;
fdout = -1;
if ((fdin = open(fin, O_RDONLY)) < 0) {
status = errno;
perror(fin);
} else if ((fdout = open(fout, O_WRONLY|O_TRUNC|O_CREAT, 0660)) < 0) {
status = errno;
perror(fout);
} else if (lseek(fdin, off, SEEK_SET) == (off_t)-1) {
status = errno;
perror("Seeking input");
} else {
while (len > 0 && (rd = read(fdin, buf, min(len, sizeof(buf)))) > 0) {
if (write(fdout, buf, rd) != rd) {
/*don't bother with partial writes or EINTR/EAGAIN*/
status = errno;
perror(fin);
break;
}
len -= rd;
}
if (rd < 0) {
status = errno;
perror(fin);
}
}
if (fdin >= 0) close(fdin);
if (fdout >= 0) close(fdout);
return status;
}
EOF
Finally, extract the .debug sections and glue them to the stripped binary.
objcopy `
objdump -h program.dbg |
awk '$2~/^\.debug/' |
while read idx name size vma lma off algn ; do
echo "$name" >&2
echo " --add-section=$name=$name.raw"
./mydd program.dbg 0x$off 0x$size $name".raw"
done
` program program_with_dbg
elfutils comes with the tool eu-unstrip which can be used to merge symbol files with executables. The result can then be used in place of the stripped version.
Valgrind supports separate debug files, so you should use the answer here, and valgrind should work properly with the externalized debug file.
I'm working with linux elf files.
I want to detect, if the given elf program is statically linked (full static link, ldd says "not a dynamic executable") or dynamically linked. The ELF is for embedded Linux, so I can't just run it or use ldd utility.
I want to do this entirely in my program, by reading and checking some bytes. I want not to depend on file utility or on libelf, binutils, etc.
Which bytes will be different?
How about using ldd.c from μClibc? It should be fairly easy to strip out any unwanted dependencies / checks if you want. I think this is a smarter approach than trying to figure out all the corner cases from reading man 5 elf, though FWIW it looks to be just checking for a PT_INTERP program header as you suspect in the comments.
Update: There's a few more checks. I've tried to extract the relevant parts, but I can't be sure if I've missed anything so check for yourself. The code checks 32-bit and 64-bit x86 ELF files. It assumes a little-endian architecture.
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
int main(int argc, char* argv[])
{
const char* fname = argv[0];
if (argc >= 2) fname = argv[1];
int fd;
struct stat st;
void *mapping;
if ((fd = open(fname, O_RDONLY)) == -1) {
perror(fname);
return 1;
}
if (fstat(fd, &st)) {
perror("fstat");
close(fd);
return 1;
}
if ((mapping = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
const Elf32_Ehdr* eh = mapping;
if (st.st_size < (off_t)sizeof(Elf32_Ehdr) ||
eh->e_ident[EI_MAG0] != ELFMAG0 ||
eh->e_ident[EI_MAG1] != ELFMAG1 ||
eh->e_ident[EI_MAG2] != ELFMAG2 ||
eh->e_ident[EI_MAG3] != ELFMAG3 ||
eh->e_ident[EI_VERSION] != EV_CURRENT) {
printf("Not a valid ELF file\n");
return 0;
}
if (eh->e_type != ET_EXEC && eh->e_type != ET_DYN) {
printf("Not executable or shared object\n");
return 0;
}
int is_dynamic = 0;
// change as appropriate, but remember that byteswapping might be needed in some cases
if (eh->e_ident[EI_CLASS] == ELFCLASS32 && eh->e_ident[EI_DATA] == ELFDATA2LSB && eh->e_machine == EM_386) {
uint16_t ph_cnt;
for (ph_cnt = 0; ph_cnt < eh->e_phnum; ph_cnt++) {
const Elf32_Phdr* ph = (const Elf32_Phdr*)((const uint8_t*)mapping + eh->e_phoff + ph_cnt * eh->e_phentsize);
if (ph->p_type == PT_DYNAMIC || ph->p_type == PT_INTERP) {
is_dynamic = 1;
}
}
} else if (eh->e_ident[EI_CLASS] == ELFCLASS64 && eh->e_ident[EI_DATA] == ELFDATA2LSB && eh->e_machine == EM_X86_64) {
const Elf64_Ehdr* eh = mapping;
uint16_t ph_cnt;
for (ph_cnt = 0; ph_cnt < eh->e_phnum; ph_cnt++) {
const Elf64_Phdr* ph = (const Elf64_Phdr*)((const uint8_t*)mapping + eh->e_phoff + ph_cnt * eh->e_phentsize);
if (ph->p_type == PT_DYNAMIC || ph->p_type == PT_INTERP) {
is_dynamic = 1;
}
}
} else {
printf("Unsupported architecture\n");
return 0;
}
munmap(mapping, st.st_size);
close(fd);
printf("%s: %sdynamic\n", fname, is_dynamic?"":"not ");
return 0;
}