I have a code to print dependencies without libelf, only by using libc here:
#include <stdio.h>
#include <string.h>
#include <elf.h>
int print_dependencies(const char *file_name)
{
Elf64_Ehdr ehdr;
Elf64_Shdr shdr, shdr_shstrtab, shdr_dynstr;
Elf64_Phdr phdr;
Elf64_Dyn dyn;
long int oldpos, dynpos;
int dyncount;
char sname[1000];
FILE *f = fopen(file_name, "r");
if(!f) { return 1; }
if(fseek(f, 0, SEEK_SET) != 0) { fclose(f); return 1; }
if(fread(&ehdr, sizeof(ehdr), 1, f) <= 0) { fclose(f); return 1; }
if(memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { fclose(f); return 1; }
if(fseek(f, ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shstrndx), SEEK_SET) != 0) { fclose(f); return 1; }
if(fread(&shdr_shstrtab, sizeof(shdr_shstrtab), 1, f) <= 0) { fclose(f); return 1; }
if(fseek(f, ehdr.e_shoff, SEEK_SET) != 0) { fclose(f); return 1; }
for(int i = 0; i < ehdr.e_shnum; i++) {
if(fread(&shdr_dynstr, sizeof(shdr_dynstr), 1, f) <= 0) { shdr_dynstr.sh_type = SHT_NULL; break; }
if(shdr_dynstr.sh_type == SHT_STRTAB) {
oldpos = ftell(f);
sname[8] = 0;
if(fseek(f, shdr_shstrtab.sh_offset + shdr_dynstr.sh_name, SEEK_SET) == 0) {
fgets(sname, 8, f);
}
if(strcmp(sname, ".dynstr") == 0) { break; } else { shdr_dynstr.sh_type = SHT_NULL; }
if(fseek(f, oldpos, SEEK_SET) != 0) { break; }
}
}
if(fseek(f, ehdr.e_shoff, SEEK_SET) == 0) {
for(int i = 0; i < ehdr.e_shnum; i++) {
if(fread(&shdr, sizeof(shdr), 1, f) <= 0) { break; }
if(shdr.sh_type == SHT_DYNAMIC) {
oldpos = ftell(f);
dyncount = shdr.sh_size / shdr.sh_entsize;
if(fseek(f, shdr.sh_offset, SEEK_SET) == 0) {
for(int i = 0; i < dyncount; i++) {
if(fread(&dyn, sizeof(dyn), 1, f) <= 0 || dyn.d_tag == DT_NULL) { break; }
if(dyn.d_tag == DT_NEEDED) {
dynpos = ftell(f);
if(fseek(f, shdr_dynstr.sh_offset + dyn.d_un.d_val, SEEK_SET) == 0) {
sname[sizeof(sname) - 1] = 0;
fgets(sname, sizeof(sname) - 1, f);
printf("shdr.sh_type is SHT_DYNAMIC, dyn.d_tag is DT_NEEDED: %s\n", sname);
}
if(fseek(f, dynpos, SEEK_SET) != 0) { break; }
}
}
}
if(fseek(f, oldpos, SEEK_SET) != 0) { break; }
}
}
}
if(fseek(f, ehdr.e_phoff, SEEK_SET) == 0) {
for(int i = 0; i < ehdr.e_phnum; i++) {
if(fread(&phdr, sizeof(phdr), 1, f) <= 0) { break; }
if(phdr.p_type == PT_DYNAMIC) {
oldpos = ftell(f);
dyncount = phdr.p_filesz / sizeof(dyn);
if(fseek(f, phdr.p_offset, SEEK_SET) == 0) {
for(int i = 0; i < dyncount; i++) {
if(fread(&dyn, sizeof(dyn), 1, f) <= 0 || dyn.d_tag == DT_NULL) { break; }
if(dyn.d_tag == DT_NEEDED) {
dynpos = ftell(f);
if(fseek(f, shdr_dynstr.sh_offset + dyn.d_un.d_val, SEEK_SET) == 0) {
sname[sizeof(sname) - 1] = 0;
fgets(sname, sizeof(sname) - 1, f);
printf("phdr.p_type is PT_DYNAMIC, dyn.d_tag is DT_NEEDED: %s\n", sname);
}
if(fseek(f, dynpos, SEEK_SET) != 0) { break; }
}
}
}
if(fseek(f, oldpos, SEEK_SET) != 0) { break; }
}
}
}
fclose(f);
return 0;
}
int main(int argc, char* argv[])
{
if(argc > 1) {
print_dependencies(argv[1]);
}
return 0;
}
This is a C code, i have tried to make it handle all errors and not cause any memory leak, fgets could be replaced by something that reads byte by byte and allocate only required amount of bytes to conserve some memory. You can use malloc and realloc based arrays to store sname to be used by caller function. C++ can be used too if desired, then you can use C++ vector for storing sname in a convenient way. This code uses fseek extensively, if you don't want to seek and read through the file many times, you can cache the file into the memory and process it from memory, or just cache revelant parts of the file. Note that if you need to know all files required to run an app, like ldd does, you need to use print_dependencies recursively inside itself, getting dependencies of dependencies etc.
foo
dynamically linkinglibbar.so
which itself is dynamically linkinglibgee.so
, soldd foo
will tell about bothlibbar.so
andlibgee.so
)? – Argenteuilreadelf -d
#6243261 , indirect withldd
: unix.stackexchange.com/questions/120015/… – Merovingian