2

I'm on a Linux environment and I need to make a program that retrieves some data that is placed in one of the sections of its executable file. So, how to get a pointer to a section of a program (by its name) from within itself?

I know it's possible to use elf_getdata() passing the index of the section as argument to get and Elf_Data struct and one of the fields of this struct is d_buf, which is a pointer to the actual data. However, it seems the elf_getdata() function makes a copy of the section data from the file to the memory and that's not what I want. I want a pointer to the data the has been loaded to the memory in loading time.

So, guys, any idea?

LuisABOL
  • 2,951
  • 4
  • 25
  • 57
  • 1
    Why not just make a global variable and reference it by name? –  Aug 28 '12 at 12:51
  • @H2CO3 Because what I'm trying to make is a dynamic library against which other programs will be linked. So, the data in that specific section will never be the same. – LuisABOL Aug 28 '12 at 12:59
  • 1
    If you try to dynamically load a shared library, you should be using e.g. [`dlopen`](http://linux.die.net/man/3/dlopen) for that, and use `dlsym` to get variables/functions from the loaded library. – Some programmer dude Aug 28 '12 at 13:04
  • @JoachimPileborg But I don't want to load a shared library in runtime time. I need to link this library against other object files (in linking time) to be able to get data placed in specific sections of those object files in runtime. So the data is not stored in the shared library. – LuisABOL Aug 28 '12 at 13:14
  • I still think it would be cleaner just to pass a pointer to the data when calling a function in your library. – jpa Aug 28 '12 at 13:23
  • Then maybe you should write a custom [linker script](http://sourceware.org/binutils/docs-2.21/ld/Scripts.html#Scripts)? – Some programmer dude Aug 28 '12 at 13:25
  • @jpa OK. But the point is, it's not me who creates the sections from which I need to retrieve data. They are automatically generated by the compiler... However, I know the name of the symbol which is the start point of the section. So, is there any way to use a symbol as a pointer in C just like we do in assembly? – LuisABOL Aug 28 '12 at 13:45
  • You can use a symbol as pointer like: `extern int mysymbol; int* pointer = &mysymbol;`. However, I'm not sure if you can reference a symbol in the main program from a separate library. – jpa Aug 28 '12 at 13:50
  • @jpa Yes, you're right. I just tried to use `extern` to reference the symbol, but it didn't work. The linker said the symbol to be undefined. – LuisABOL Aug 28 '12 at 13:58
  • Can't you `dlopen` the whole program itself, use `dlsym` (perhaps with `dladdr`?) on the whole program handle, etc. – Basile Starynkevitch Aug 28 '12 at 14:26
  • I still don't see why a global variable isn't sufficient. –  Sep 01 '12 at 20:07

2 Answers2

5

Actually, using libelf, it's possible to use the Elf64_Shdr struct (for 64-bit systems) to get a pointer to a section, because the sh_addr field do points to the actual adress where the section will be loaded in runtime. So, it can be used as a pointer. This way, it's not even necessary to use the elf_getdata() function to retrieve a Elf_Data struct.

Since what I want to do is a library which other object files can be linked against, my code may have a function which opens the executable file itself to make use of some libelf features, so that it can read data from the main file sections, as follows:

// A global variable which stores the executable file name
extern const char *__progname;

void retrieve_data() {
  int fd;       // File descriptor for the executable ELF file
  char *section_name, path[384];
  size_t shstrndx;

  Elf *e;           // ELF struct
  Elf_Scn *scn;     // Section index struct
  Elf64_Shdr *shdr;     // Section struct

  // Create the full path of the executable
  getcwd(path, 255);
  strcat(path, "/");
  strncat(path, __progname, 127);

  if (elf_version(EV_CURRENT) == EV_NONE)
    errx(EXIT_FAILURE, "ELF library iinitialization failed: %s", elf_errmsg(-1));

  if ((fd = open(path, O_RDONLY, 0)) < 0)
    err(EXIT_FAILURE, "open \"%s\" failed", path);

  if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
    errx(EXIT_FAILURE, "elf_begin() failed: %s.", elf_errmsg(-1));

  // Retrieve the section index of the ELF section containing the string table of section names
  if (elf_getshdrstrndx(e, &shstrndx) != 0)
    errx(EXIT_FAILURE, "elf_getshdrstrndx() failed: %s.", elf_errmsg(-1));

  scn = NULL;

  // Loop over all sections in the ELF object
  while ((scn = elf_nextscn(e, scn)) != NULL) {
    // Given a Elf Scn pointer, retrieve the associated section header
    if ((shdr = elf64_getshdr(scn)) != shdr)
      errx(EXIT_FAILURE, "getshdr() failed: %s.", elf_errmsg(-1));

    // Retrieve the name of the section
    if ((section_name = elf_strptr(e, shstrndx, shdr->sh_name)) == NULL)
      errx(EXIT_FAILURE, "elf_strptr() failed: %s.", elf_errmsg(-1));

    // If the section is the one we want... (in my case, it is one of the main file sections)
    if (!strcmp(section_name, "Section name")) {
      // We can use the section adress as a pointer, since it corresponds to the actual
      // adress where the section is placed in the virtual memory
      struct data_t * section_data = (struct data_t *) shdr->sh_addr;

      // Do whatever we want

      // End the loop (if we only need this section)
      break;
    }
  }

  elf_end(e);
  close(fd);
}
LuisABOL
  • 2,951
  • 4
  • 25
  • 57
  • 1
    only that "name" should be "section_name" – Sasha Nicolas Feb 07 '19 at 20:09
  • If you're going to unconditionally append stuff after `getcwd(path, 255);` you need a bigger buffer, or you need to pass a size less than `255`, or use `strncat`. Otherwise this is a buffer overflow waiting to happen. (IMO anything people could copy/paste from an SO answer should be safe and correct, or at least commented if avoiding subtle bugs would be too bloated.) – Peter Cordes Feb 08 '19 at 08:38
  • @PeterCordes Agreed! Fixed. – LuisABOL Feb 10 '19 at 11:15
4

ELF headers are not necessarily mapped into the memory, so relying on it might be a bit risky. Here's a way that doesn't require any APIs:

  1. Declare two symbols denoting start and end of the section in question.
  2. use __attribute__ ((section (""))) to place them in sections with known names
  3. use a custom linker script that puts the section you need between the two variables' sections.

Alternatively, you can use linker-generated symbols, the way it's done for _etext/_edata/_end.

Igor Skochinsky
  • 24,629
  • 2
  • 72
  • 109
  • Ok. But the point is, it's not me who creates the sections from which I need to retrieve data. They are automatically generated by the compiler... However, I know the name of the symbol which is the start point of the section. So, is there any way to use a symbol as a pointer? – LuisABOL Aug 28 '12 at 13:36
  • But I tried to reference the symbol with `extern` and it didn't work. The linker said the symbol to be undefined... – LuisABOL Aug 28 '12 at 14:01
  • How do you know that the symbol exists? How are you declaring it and referencing it? – Igor Skochinsky Aug 28 '12 at 16:00
  • I know the symbol exists because it is in the assembly source generated from the main file (gcc -S ...). I'm declaring it in the library file with `extern struct type_x * my_symbol;` to try to reference it from the main file. But I think it is not being identified from the library file because it is in the main file and doesn't have the `.globl` assignment in the assembly source, so it can't be seen from outside of that file... – LuisABOL Aug 28 '12 at 16:14
  • 2
    This is getting nowhere. I think you should edit the question and describe what you *actually* need to do (not what you think you should do), and provide a lot more details, e.g. what's you code, what isn't and how it all connects together. – Igor Skochinsky Aug 28 '12 at 16:52