0

I have seen questions similar to this one on here before but I was unable to solve my problem from the answers to those.

I have a file libfoo.c from which I'm creating a shared object. Using gcc's __attribute__((constructor)), I'd like to print a message to a file when this shared object is loaded:

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

static char *term = NULL;

static void init (void) __attribute__((constructor));

static void
init (void)
{
  FILE *f = fopen ("lib.log", "w");

  if (f)
    {
      if (isatty (STDOUT_FILENO))
        {
          char *tmp = ttyname (STDERR_FILENO);
          if (tmp && *tmp)
            {
              term = strdup (tmp); // nevermind the memory leak
              fprintf (f, "Found terminal %s\n", term);
            }
        }
      else
        fprintf (f, "Failed to find terminal\n");

      fclose(f);
   }
}

void *
malloc (size_t size)
{
  return NULL;
}

The dummy malloc implementation (to be extended later) should replace stdlib.h's malloc in another dummy program, whose source code resides in main.c:

#include <stdlib.h>

int main (void)
{
  char *m = malloc(1024);
  (void)m;
  return 0;
}

I'm compling and linking both files as follows:

gcc -o main main.c
gcc -fpic -shared -o libfoo libfoo.c

And then when I execute the following no file is created and no output is logged:

LD_PRELOAD=$(readlink -f libfoo) ./main

What is going on here? As a sidenote: how could I try to debug a problem like this using ltrace? ltrace "LD_PRELOAD=... ./main" is not a valid command.

EDIT: What is going on here? It seems at least printf works inside the shared object, but only inside the setup function, if I call printf inside malloc the program segfaults. In trying to at least figure out what terminal stdout is connected to I did the following (inside the setup function):

// ...

char buf[100];
buf[readlink ("/proc/self/fd/1", buf, 100)] = '\0';
printf ("STDOUT (buf): %s\n", buf);

char *tmp = ttyname (1);
printf ("STDOUT (ttyname): %s, %s\n", tmp, strerror (errno));

// ...

This prints:

STDOUT (buf): /dev/pts/1
STDOUT (ttyname): (null), Success

According to the ttyname manpage, that second line of output should be impossible. Am I completely misunderstanding something here?

jww
  • 97,681
  • 90
  • 411
  • 885
Peter
  • 2,919
  • 1
  • 16
  • 35
  • 1
    At a guess, your `init` would produce no output if the `fopen` failed. Can you confirm either way? – Craig Estey Jul 13 '18 at 22:12
  • You might also try: `ltrace env LD_PRELOAD=... ./main` – Craig Estey Jul 13 '18 at 22:19
  • Yes that works and the correct malloc implementation is called, I just realized that I can simply use `strace` and system calls performed inside the shared object will be printed as well so I will try finding the problem like that. – Peter Jul 14 '18 at 06:55
  • It seems that fopen does not perform any system calls at all, if I instead use `open("lib.log", O_WRONLY | O_CREATE, 0666)`, the file is created as expected. This seems weird, I'm not sure how `fopen` is implemented internally, I will look into that. – Peter Jul 14 '18 at 07:07
  • I've found `strace` to be more useful that `ltrace`. In the init, it may be too early for `fopen` to work because it is more weighty (needs more resources/symbols to be loaded/linked) than the `open` function (which is just a thin wrapper around the syscall). In your `malloc`, remember that `printf` may call `malloc`, so you may need an `i_am_already_in_malloc` state flag to handle that case (i.e. to suppress the `printf` call). Otherwise, you might get infinite recursion. – Craig Estey Jul 14 '18 at 17:44
  • As to `ttyname`, it does what your code does. But, it calls `malloc`, and it can return null if `malloc` returns null, and it might be calling _your_ `malloc`. Try using `ttyname_r` which [AFAICT] doesn't call `malloc` – Craig Estey Jul 14 '18 at 17:52
  • printf recursion seems to have been the reason my program originally crashed, I had just assumed printf would use static memory internally, thank you for the hint I think I have everything figured out now. – Peter Jul 16 '18 at 10:16

1 Answers1

0

here is what I ran, created from your code, and it ran perfectly, generating a file: lib.log

The contents of the file: lib.log

Found terminal /dev/pts/1

and now, the code I ran:

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

static char *term = NULL;

static void init (void) __attribute__((constructor));

static void init (void)
{
    FILE *f = fopen ("lib.log", "w");

    if (f)
    {
        if (isatty (STDOUT_FILENO))
        {
            char *tmp = ttyname (STDERR_FILENO);
            if (tmp && *tmp)
            {
                term = strdup (tmp); // nevermind the memory leak
                fprintf (f, "Found terminal %s\n", temp);
            }
        }

        else
            fprintf (f, "Failed to find terminal\n");

      fclose(f);
   }

   else
   {
       perror( "fopen failed" );
   }
}


void *
myMalloc (size_t size)
{
    (void)size;
    return NULL;
}


int main (void)
{
    char *m = myMalloc(1024);
    (void)m;
    return 0;
}

Of course, your posted code is missing a header file. Such a header file should be referenced/included by your main.c file and your libfoo.c file

user3629249
  • 16,402
  • 1
  • 16
  • 17
  • Of course that works, but it's missing the point. I want to produce a shared library which is able replace calls to malloc in arbitrary programs (similar to valgrind) and which is able to write logs to a file. – Peter Jul 14 '18 at 06:31