[PATCH] D38513: [LLD] [RFC] [COFF] Add support for GNU binutils import libraries

Shoaib Meenai via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 12 22:52:02 PDT 2017


smeenai added a comment.

I looked into this some more. MinGW import libraries don't appear to contain the null directory table entry that's supposed to terminate the import directory table. This doesn't cause usually cause problems for link.exe, because you'll probably be linking against a non-MinGW import library, which provides the null entry (as `.idata$3`). Even if you're only linking against a MinGW import library, it looks like the loader doesn't require the entire terminating entry to be null; if either the Name RVA or the IAT RVA is null, the entry is considered to be a terminator. You can craft examples which cause problems though. All of the below discussion assumes **32-bit** VS tools and MinGW, for reasons that will become apparent later.

Create a file `fs.c`:

  __declspec(noreturn) void __stdcall ExitProcess(unsigned exitCode);
  
  __declspec(dllexport) int f() { return 1; }
  __declspec(dllexport) int g() { return 2; }
  __declspec(dllexport) int h() { return 3; }
  __declspec(dllexport) int i() { return 4; }
  __declspec(dllexport noreturn) void ex(unsigned it) { ExitProcess(it); }

Create a library from it with MSVC:

  cl /LD /MD /O1 fs.c

Create a file `fsmain.c`:

  __declspec(dllimport) int f();
  __declspec(dllimport) int g();
  __declspec(dllimport) int h();
  __declspec(dllimport) int i();
  __declspec(dllimport noreturn) void ex();
  
  void main() { ex(f() + g() + h() + i()); }

We're gonna wanna only link this with a single import library, so let's do that with the cl-generated import library first and convince ourselves that it works:

  cl /O1 /Zl fsmain.c fs.lib /link /entry:main
  fsmain
  echo %ERRORLEVEL%
  (outputs 10)

Now create a def file `fs.def`:

  LIBRARY fs
  EXPORTS
    f
    g
    h
    i
    ex

Create an import library with MinGW and see our executable fail to start properly when linked against that import library:

  dlltool -d fs.def -l fs.lib
  cl /O1 /Zl fsmain.c fs.lib /link /entry:main
  fsmain
  (you should get an "application was unable to start correctly" error)

What's happening here is that the ILT is placed immediately after the first (and only) import directory table entry, so the loader tries to read the ILT as if it were a directory entry, and we have enough ILT entries that both the Name RVA and IAT RVA fields are non-null, so the directory entry is considered non-null, and you get a crash from the malformed entry.

You can alter the number of exports in the def file (and adjust the expression in `main` accordingly) to see how the loader is behaving. Only exporting `ex` should work because the IAT RVA (but not the Name RVA) will end up being null. Exporting `ex` and `f` will crash because both fields will end up non-null. Exporting `ex`, `f`, and `g` will work because the Name RVA (but not the IAT RVA) will end up being null. Exporting `ex`, `f`, `g`, and `h` will work because the IAT RVA (but not the Name RVA) will end up being null.

This is also why it's important to use 32-bit tools. With 64-bit tools, unless you have a huge import library, the upper 4 bytes of each ILT entry will be 0, so you'll always end up with a Name RVA of 0, and the problem will be masked.


https://reviews.llvm.org/D38513





More information about the llvm-commits mailing list