Can a program read its section of an elf?

I would like to use the ld -build-id option to add build information to my binary. However, I am not sure how to make this information available inside the program. Suppose I want to write a program that writes a back trace every time an exception occurs, and a script that parses this information. The script reads the program’s symbol table and looks for addresses printed in backtrace (I have to use such a script because the program is statically linked and backtrace_symbols does not work). For the script to work correctly, I need to match the build version of the program with the build version of the program that created the back trace. How can I print the version of the program assembly (located in the .note.gnu.build-id elf section) from the program itself?

+6
source share
2 answers

How can I print the version of the program assembly (located in the .note.gnu.build-id elf section) from the program itself?

  • You need to read ElfW(Ehdr) (at the beginning of the file) to find the program headers in your binary format ( .e_phoff and .e_phnum will tell you where the program headers are and how many of them to read).

  • You then read the program headers until you find the PT_NOTE segment of your program. This segment will notify you of the start offset of all notes in your binary format.

  • Then you need to read ElfW(Nhdr) and skip the rest of the note (total size of note sizeof(Nhdr) + .n_namesz + .n_descsz correctly aligned) until you find the note with .n_type == NT_GNU_BUILD_ID .

  • After you find the note NT_GNU_BUILD_ID , skip it .n_namesz and read the bytes .n_descsz to read the actual assembly ID.

You can verify that you are reading the correct data by comparing what you are reading with the output of readelf -n a.out .

PS

If you are going to solve the build-id decryption problem as described above, and if your executable file is not deleted, you might be better off simply decoding and printing the symbol names instead (i.e. replicate what backtrace_symbols ) - actually in fact, it’s easier to do than decode ELF notes, because the symbol table contains fixed-size records.

+5
source

Basically, this is the code that I wrote based on the answer to my question. In order to compile the code, I had to make some changes, and I hope that it will work as many types of platforms as possible. However, it was tested on only one machine. One of the assumptions I used was that the program was built on a machine that runs it, so you should not check compatibility between the program and the machine.

 user@ :~/$ uname -s -r -m -o Linux 3.2.0-45-generic x86_64 GNU/Linux user@ :~/$ g++ test.cpp -o test user@ :~/$ readelf -n test | grep Build Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc user@ :~/$ ./test Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc 
 #include <elf.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #if __x86_64__ # define ElfW(type) Elf64_##type #else # define ElfW(type) Elf32_##type #endif /* detecting build id of a program from its note section http://stackoverflow.com/questions/17637745/can-a-program-read-its-own-elf-section http://www.scs.stanford.edu/histar/src/pkg/uclibc/utils/readelf.c http://www.sco.com/developers/gabi/2000-07-17/ch5.pheader.html#note_section */ int main (int argc, char* argv[]) { char *thefilename = argv[0]; FILE *thefile; struct stat statbuf; ElfW(Ehdr) *ehdr = 0; ElfW(Phdr) *phdr = 0; ElfW(Nhdr) *nhdr = 0; if (!(thefile = fopen(thefilename, "r"))) { perror(thefilename); exit(EXIT_FAILURE); } if (fstat(fileno(thefile), &statbuf) < 0) { perror(thefilename); exit(EXIT_FAILURE); } ehdr = (ElfW(Ehdr) *)mmap(0, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(thefile), 0); phdr = (ElfW(Phdr) *)(ehdr->e_phoff + (size_t)ehdr); while (phdr->p_type != PT_NOTE) { ++phdr; } nhdr = (ElfW(Nhdr) *)(phdr->p_offset + (size_t)ehdr); while (nhdr->n_type != NT_GNU_BUILD_ID) { nhdr = (ElfW(Nhdr) *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz + nhdr->n_descsz); } unsigned char * build_id = (unsigned char *)malloc(nhdr->n_descsz); memcpy(build_id, (void *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz), nhdr->n_descsz); printf(" Build ID: "); for (int i = 0 ; i < nhdr->n_descsz ; ++i) { printf("%02x",build_id[i]); } free(build_id); printf("\n"); return 0; } 
+2
source

Source: https://habr.com/ru/post/949397/


All Articles