Bug 868662

Summary: /lib/ld64.so.1: bad ELF Interpreter: No such file or directory
Product: [Fedora] Fedora Reporter: tim <tim>
Component: binutilsAssignee: Nick Clifton <nickc>
Status: CLOSED NOTABUG QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: low Docs Contact:
Priority: unspecified    
Version: 17CC: jakub, nickc
Target Milestone: ---   
Target Release: ---   
Hardware: x86_64   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2012-10-27 06:54:45 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description tim 2012-10-21 14:19:29 UTC
Description of problem:
I'm playing around with 64 bit assembly (using nasm) and I've encountered some problems getting my executable to link correctly.
When I use
  ld hello.o -o hello -lc
I get this output when running ./hello:
  bash: ./hello: /lib/ld64.so.1: bad ELF interpreter: No such file or directory
Now I found an example where this was used
  ld hello.o -o hello -lc --dynamic-linker /lib/ld-2.7.so
I tried that, but without success:
  bash: ./hello: /lib/ld-2.7.so: bad ELF interpreter: No such file or directory
But this got me started in a search for the right linker, but it took me a while to figure out that the 64 bit version was not in /lib at all, but in /lib64, the correct way to assemble and link (afaik) then being:
$ nasm -f elf64 -o hello.o -l hello.lst hello.nasm 
$ ld hello.o -o hello -lc --dynamic-linker /lib64/ld-2.15.so 
$ ./hello
Hello, world!

Now perhaps this was intended and I was supposed to use this anyway, but I was wondering why ld uses /lib/ld64.so.1 as default if that file doesn't exist (or at least it doesn't seem to exist in fedora 17).
Shouldn't it use /lib64/ld-2.15.so by default if no other linker is specified? And how should I link an executable that is supposed to be distributed to a system where the linker may not have that exact version number or where the linker is in an other directory (clearly, there is some difference between systems in which file to use)


Version-Release number of selected component (if applicable):
GNU ld version 2.22.52.0.1-10.fc17 20120131

How reproducible:
Always

Steps to Reproduce:
1. Assemble x86-64 assembly (e.g. "hello world") program that uses libc with elf64 format (example below)
2. Link executable using ld <object file> -o <executable-name> -lc
3. Run the executable. Get this error:
  bash: ./hello: /lib/ld64.so.1: bad ELF interpreter: No such file or directory
4. Link again, overriding the default linker setting:
  
Actual results:
Getting an error when using default setting, having to use --dynamic-linker option when using ld.

Expected results:
Default setting should suffice.

Additional info:
The same thing does not occur when I don't use libc, I guess that it doesn't need to use the dynamic linker at runtime if everything is staticly linked. Correct?

Source:
SECTION .data
msg:    db "Hello, world!", 0
fmt:    db "%s", 10, 0

SECTION .text
        extern printf
        global _start

_start:
        mov esi, msg    ; 64-bit ABI passing order starts w/ edi, esi, ...
        mov edi, fmt    ;
        mov eax, 0      ; printf is varargs, so EAX counts # of non-integer arguments being passed
        call printf

        ; Exit from the application
        mov     rax,0x3c ; Exit function
        xor     rdi,rdi  ; Exit code
        syscall

Comment 1 Nick Clifton 2012-10-23 10:27:24 UTC
Hi Tim,

> When I use
>   ld hello.o -o hello -lc

Generally speaking this is a bad idea.  Unless you are really
confident with using the linker it is a better idea to let gcc create
the linker command line for you.  Ie:

  gcc -nostartfiles hello.o -o hello -lc
  
> Now perhaps this was intended and I was supposed to use this anyway,
> but I was wondering why ld uses /lib/ld64.so.1 as default if that
> file doesn't exist (or at least it doesn't seem to exist in fedora
> 17).

That's the reason.  The default is setup for a generic system not
Fedora.

> Shouldn't it use /lib64/ld-2.15.so by default if no other linker is
> specified?

Only if that interpreter exists.  On Fedora 15 for example interpreter
would be /lib64/ld-2.14.so.  This is why using gcc to drive the linker
is usually the best idea.

I take your point however that the default interpreter could be made
release specific, changing with each release as necessary.  But that
seems like a lot of hassle to me.  If you are trying to drive the
linker directly, then you should be able to choose the correct
interpreter for yourself and not have to rely upon builtin defaults.

Cheers
  Nick

Comment 2 Jakub Jelinek 2012-10-23 10:30:24 UTC
I think nothing should change in binutils, this is a clear user error.
ld shouldn't be used directly if people don't know what they are doing.

Comment 3 tim 2012-10-23 22:31:48 UTC
Hi.. Thanks for the replies.

I'm not suggesting a change to binutils. Or not a big one anyway.. But look at the bigger picture. Isn't it possible to symlink /lib/ld64.so.1 to /lib64/ld-2.15.so so that the default setting would still work? As I understand, dynamic libraries use the same approach so that applications may be compiled against somelib.1.2 and use everything from 1.2.0.0 to 1.2.99.999 as long as the system on which they run has the symlink for somelib.1.2 linked to the right version (provided some version of that lib doesn't break compatibility of course)

Jakub: I understand why you say that people shouldn't use ld if they don't know what they're doing, but if no one would ever experiment with it once in a while, who are we going to ask in the future when we need an expert on the subject? I suppose that you learned a thing or two through experimenting.. :)

I agree it was (even kinda obviously) a user error. But I don't want to use gcc like the rest of the plebs because I'm writing a compiler and so I'd like to learn as much as I can about ld and stuff around it. Could be I'm using the wrong approach and I should link my compiler with a library that implements the linker (perhaps that same ld-2.15.so?) so that my program can produce an executable without having to produce a temp file and call the linker as a separate process.

Anyway, I thought this could have been a problem with the way binutils was configured (been given an incorrect path/filename) while running ./configure. But thinking back, I realize that any such thing would probably have been noticed much earlier through much more severe problems (unless everything in Fedora is statically linked, which I don't really expect :D)


> Only if that interpreter exists.  On Fedora 15 for example interpreter
> would be /lib64/ld-2.14.so.  This is why using gcc to drive the linker
> is usually the best idea.


Nick: Thanks for clearing up some of the fog.. I'm not entirely sure we understand each other though. Clearly (at least to me at the moment), the ld process links the executable in such a way that the path/filename to the interpreter is included in the executable header somewhere or it is some initialization code that tries to call the interpreter or something, but the path/filename seems to be hard coded anyway.
So clearly, the ld process either has that path/filename hardcoded as well or uses some setting for it. Either way, it encodes /lib/ld64.so.1 as the path/filename for the interpreter to use into the executable. But what good would that be if the interpreter on every system may be a different version or even in a different directory?

What I'm trying to say is that, as most distributions probably are stubborn enough to put the interpreter somewhere else and don't provide a symlink (if that approach is at all possible) to the right file, what use is that default ever going to be? So you might as well change it and have it match the interpreter for the system you're using it on.. (I don't know if it is possible to change it at all though)

It certainly saves having to override the default every time one uses/experiments with ld directly and it would also help some people who are new to linux software development to get started with the whole circus of makefiles, automake, ./configure and all other complexities they might not be used to, for example when coming from a luxury IDE on some other platform where they just need to press F5/F9 to run their application.. :)

Greetz,
Tim.

Comment 4 Jakub Jelinek 2012-10-24 07:07:37 UTC
If you are writing a compiler targetting x86_64, you should spend time to find out what other compilers are passing to the linker and why, read the corresponding psABIs etc.  You'd learn the name of the x86_64-linux dynamic linker is /lib64/ld-linux-x86-64.so.2, for which there is a symlink maintained in glibc and would pass -dynamic-linker /lib64/ld-linux-x86-64.so.2 if creating binaries for x86_64-linux.  Similarly for all other targets you want to support.  There are many other ld options that are desirable to pass, -dynamic-linker isn't the only one.

Comment 5 Nick Clifton 2012-10-25 11:30:23 UTC
Hi Tim,

> understand each other though. Clearly (at least to me at the moment), the ld
> process links the executable in such a way that the path/filename to the
> interpreter is included in the executable header somewhere

It is.  Have a look at the .interp section in the executable.

> So clearly, the ld process either has that path/filename hardcoded as well
> or uses some setting for it.

Take a look at the definition of ELF64_DYNAMIC_INTERPRETER in bfd/elf64-x86-64.c in the binutils sources.
 
> What I'm trying to say is that, as most distributions probably are stubborn
> enough to put the interpreter somewhere else and don't provide a symlink (if
> that approach is at all possible) to the right file, what use is that
> default ever going to be? So you might as well change it and have it match
> the interpreter for the system you're using it on.

So you are suggesting a patch similar to this one (although extended to cover other architectures, not just the x86_64):

*** ../binutils-2.23.51.0.3.orig/bfd/elf64-x86-64.c     2012-10-25 12:14:31.077299227 +0100
--- bfd/elf64-x86-64.c  2012-10-25 12:14:49.874307932 +0100
*************** elf_x86_64_write_core_note (bfd *abfd, c
*** 508,515 ****
  /* The name of the dynamic interpreter.        This is put in the .interp
     section.  */
  
! #define ELF64_DYNAMIC_INTERPRETER "/lib/ld64.so.1"
! #define ELF32_DYNAMIC_INTERPRETER "/lib/ldx32.so.1"
  
  /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
     copying dynamic variables from a shared lib into an app's dynbss
--- 508,515 ----
  /* The name of the dynamic interpreter.        This is put in the .interp
     section.  */
  
! #define ELF64_DYNAMIC_INTERPRETER "/lib64/ld-2.15.so"
! #define ELF32_DYNAMIC_INTERPRETER "/lib/ld-2.15.so"
  
  /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
     copying dynamic variables from a shared lib into an app's dynbss


The problem with this is that the patch will have to be updated whenever the dynamic interpreter is changed.  Since the interpreter is not part of the binutils package that means that someone (= probably me...) will have to monitor every glibc change to see if it alters the interpreter.  Which is a pain, and not something that I really want to do.

Also, another point is that having the wrong default dynamic interpreter name built into the linker can actually be helpful.  It started you on your search for why your program did not run, and I am sure that you would agree that you have learnt a lot in the process.

So, this is what I will do.  I am not going to apply this patch just for you.  But, if some more people complain, and agree that the patch should go in, then I will bow to public pressure and add it to the binutils rpm.

Cheers
  Nick

Comment 6 tim 2012-10-26 23:50:10 UTC
Hi Nick, Jakub,

Tnx for the excellent replies. That patch was more or less what I was suggesting. IMHO it makes more sense not to confuse the people who are new to this sort of trickery.. There is a lot of incorrect info out there that is easily found through google, (as I noticed when most examples I found seem to suggest using ld in stead of gcc - and even use it incorrectly). Though it may be I've only seen the problem cases or I misunderstood the situation, I don't think the philosophy should be to make it harder than it needs to be if it can be avoided.

I see what you're saying though. I wouldn't want to have to keep an eye on every dependency just in case something changes and I'd need to change everything that depends on those changes. Surely no one can expect you guys to maneuver yourselves into dependency hell. And I guess the symlink approach that I suggested is just an easier, bit of a workaround to basically do the same thing (with the difference that binaries that had this default setting would still be guided at runtime to the right interpreter, but that's a minor advantage I guess).. Still, you'd need to change the symlink each time the interpreter changes so it doesn't matter much in terms of maintenance..

I wonder if there could be some way this could be automated so you wouldn't have to think about it. Somewhat like the akmod package for nvidia that takes care of keeping the kernel module up to date after receiving a kernel update. This could detect if the version/filename/location for the interpreter(s) change and update these lines to match the right files..
#define ELF64_DYNAMIC_INTERPRETER "/lib/ld64.so.1"
#define ELF32_DYNAMIC_INTERPRETER "/lib/ldx32.so.1"

Or, if the /lib64/ld-linux-x86-64.so.2 symlink is maintained by glibc, shouldn't the ELF64_DYNAMIC_INTERPRETER macro simply be set to that?

I don't think it's a big issue though. It's true that through this trouble, I've learned some things and I appreciate that. But you know, I have a fulltime job as software developer next to doing a little development at home. So with the little time I have left next to my job, I don't want to spend most of that going through heaps of documentation that generally seem to tell me lots of interesting stuff but not what I want to know at that moment. But make no mistake: I *do* use books and documentation, but only to look something up when I need to (and I didn't expect to need to know specifics of ld at this moment).

The mistakes that I made with ld here was partly because I was used to that approach from previous experiments a long time ago (and even then I probably got it from an example on some forum) and because I didn't want to depend on gcc, but also not wanting to spend a lot of time figuring out the exactly right way to use ld.

It's like I would tell someone I want to use linux and they would recommend me to use linux-from-scratch, which basically is just a manual explaining how to build yourself a working linux environment from source). But I don't want to build one, I want to use one (which is why I use Fedora - more or less comfortable for the somewhat power user, but still easy and fast to get up and running).

In this case I was excited about doing some experiments with x64 assembly and in stead I had to get the problems with linking out of the way first. I think this is one of the bigger reasons why a lot of people try linux and quickly give up on it. They don't want to spend hours fixing other problems than they were planning to tackle. Which is why I was very annoyed I had to find an alternative to gnome because I didn't want to learn a new desktop philosophy when I was perfectly happy with gnome 2. Yes, I could probably have downloaded the gnome 2 sources or MATE or something alike, but I chose to settle for XFCE because once again, I want to spend my time working on my own ideas. I read once that linux is about freedom, but I don't quite agree. I'd ask whose freedom: that of the user or that of the developer? It can't be the freedom of the user if they were forced to move from gnome 2 to gnome 3.

Anyway thanks a lot guys.. I think we can consider this issue closed if you agree.

Cheers,
Tim.

Comment 7 Jakub Jelinek 2012-10-27 06:54:45 UTC
ld is a linker not just for Linux, but for various other OSes too.  As such, hardcoding in it the Linux name of the dynamic linker, when e.g. psABI says differently, is IMHO a bad idea.  The binutils sources usually default to whatever the particular psABI requires.  The standard place where the Linux dynamic linker name is hardcoded is the compiler driver.