<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - LLD may incorrect mark dynamic executable as PT_EXEC (static executable) in some situations."
   href="https://bugs.llvm.org/show_bug.cgi?id=48035">48035</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>LLD may incorrect mark dynamic executable as PT_EXEC (static executable) in some situations.
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>lld
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>unspecified
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Linux
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>ELF
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>harley.paterson@postgrad.otago.ac.nz
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org, smithp352@googlemail.com
          </td>
        </tr></table>
      <p>
        <div>
        <pre># Description

LLVM marks the ELF header of outputs all executable files with program type
executable `PT_EXEC` (executable object), including dynamically linked programs
and relocatable executable. My understanding is `PT_EXEC` is reserved for
statically linked, non-relocatable executable.

"PT_DYN" (dynamic object) is used for any code which requires the loader to
manipulate the address space after segments are loaded - including relocatable
executables, executables with dynamically loaded dependencies, and actual
dynamic libraries - Even if these "dynamic objects" are actually executable
programs with entry points.

Several GNU libc utilities, `ld-linux.so`, and the `dlfcn.h` functions assume
this scheme.

LLVM's scheme has at lease two observable problems:

- The loader, at least on Linux, will not apply address space layout
randomization. I've not tested this on other platforms.
- Glibc `dlopen` will refuse to open any object marked as `PT_EXEC`, even if
the object contains dynamic symbols and is relocatable.

I believe both the issues reported in the following test cases would be fixed
by the LLVM linker using the `PT_DYN` type for any executable which is position
independent, requires relocation, or requires dynamic linking.

# Test case

test.c
```c
#include <stdlib.h>
#include <stdio.h>

void alpha()
{
    printf("Alpha function at 0x%p\n", &alpha);
}

int main(void)
{
    alpha();
    return(EXIT_SUCCESS);
}
```

## Test 1: ALSR not applied:
```sh
# LLVM's behavior
$ clang -fPIE test.c -o test
$ readelf -h test | grep "Type"
Type:                              EXEC (Executable file)
$ ./test
Alpha function at 0x4004d0    # ASLR not applied.
$ ./test
Alpha function at 0x4004d0    # ASLR not applied.

# Expected behavior (GCC)
$ gcc -fPIE test.c -o test
$ readelf -h test | grep "Type"
Type:                              DYN (Shared object file)
$ ./test
Alpha function at 0x0x7f877820064a    # ASLR applied.
$ ./test
Alpha function at 0x0x7f0b32e0064a    # ASLR applied.
```

## Test 2: dlopen refuses to load objects.

```sh
$ cat load.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    if (argc < 2) {
        printf("Usage: ./load <object>\n");
        exit(EXIT_FAILURE);
    }   
    void* test_handle = dlopen(argv[1], RTLD_LAZY);
    if (test_handle == NULL) {
        printf("Dlopen Could not open '%s' object.\n", argv[1]);
    }
    void* alpha_ptr = dlsym(test_handle, "alpha");
    if (alpha_ptr == NULL) {
        printf("Dlopen could not locate the 'alpha' symbol.\n");
    }
    printf("Dlopen found alpha at 0x%p\n", alpha_ptr);
    exit(EXIT_SUCCESS);
}

$ clang -ldl load.c -o load

# LLVM's behavior.
$ clang test.c -o test -rdynamic
$ readelf -h test | grep "Type"
Type:                              EXEC (Executable file)
# Dynamic symbols are present, but dlopen gives up at `PT_EXEC`.
$ readelf --dyn-syms test | grep "alpha"
5: 00000000004006c0    36 FUNC    GLOBAL DEFAULT   12 alpha
$ ./load ~/test
Dlopen Could not open '/home/hpaterson/test' object.
Dlopen could not locate the 'alpha' symbol.
Dlopen found alpha at 0x(nil)

# Expected behavour (GCC)
$ gcc -rdynamic test.c -o test
$ readelf -h test | grep "Type"
Type:                              DYN (Shared object file)
$ readelf --dyn-syms test | grep "alpha"
8: 000000000000082a    31 FUNC    GLOBAL DEFAULT   14 alpha
$ ./load ~/test
Dlopen found alpha at 0x0x7f5b225e082a
```</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>