Ubuntu: How to Link a Binary

Ubuntu: How to link a binary

Ubuntu sets your PATH environment variable to include ~/bin. So the easiest way to make xyz executable from anywhere is move xyz to ~/bin, or to make a symlink from ~/bin to the xyz's directory:

ln -s /path/to/xyz/directory/ ~/bin

Or, you could add /path/to/xyz/directory/ to your PATH environment variable.

How can I change the binary file link to something else

The command run when you type python is determined primarily by the setting of your $PATH. The first executable file called python that is found in a directory listed on your $PATH will be the one executed. There is no 'link' per se. The which command will tell you what the shell executes when you type python.

If you want python to open a different program, there are a number of ways to do it. If you have $HOME/bin on your $PATH ahead of /usr/bin, then you can create a symlink:

ln -s /usr/bal/bla/python2.7 $HOME/bin/python

This will now be executed instead of /usr/bin/python. Alternatively, you can create an alias:

alias python=/usr/bal/bla/python2.7

Alternatively again, if /usr/bal/bla contains other useful programs, you could add /usr/bal/bla to your $PATH ahead of /usr/bin.

There are other mechanisms too, but one of these is likely to be the one you use. I'd most probably use the symlink in $HOME/bin.

Link libraries to linux biinary file in c++

That's because the dynamic linker loading runtime dependencies looks for them in some specified locations, which are "by default" your system library directories (where those libraries got installed by apt).

The other user should ideally install those libraries too (which could be done "automatically" if you build a .deb package with proper dependencies)

Otherwise you would have to change the runpath of your program by adding -Wl,-rpath='$ORIGIN', which makes the dynamic linker look for dependencies just where the binary is located.

$ORIGIN here is a special variable meaning "this executable" which is what you wanted to achieve.

see rpath
and A description of RPATH $ORIGIN

Why do I have to use ld to link my binary

Why do I have to use the linker ld?

You don't have to use ld, but it's much easier to use it than not (see below).

What exactly is wrong with the bin file?

Nothing in the .bin file tells the OS how to load and run it.

At what address should the OS map this file into memory? Where should it set the RIP register to start executing it?

When you create a program which will run on a "bare metal" system, you can arrange for this program to be loaded at exactly the processor reset address, and the processor will start fetching and executing instructions from that address when it is powered on.

But you aren't trying to do that -- you are using Linux, and it needs to be told where to load the program and how to start it.

Usually this info is supplied by the ELF file header and program headers (which the linker prepares), although Linux can execute other file formats as well.

Can I do something to make it run without ld?

Sure. You can supply all the ELF header and program header bits without involving a linker at all. Example. It's just much harder to do correctly, and to debug when things go wrong.

Linux linking to any available library version

In the magical Linux world it is very difficult to compile and link on one machine and then copy the binary to another machine and run it there. There are a lot of variations, as you have also experienced not all aer compatible with each other, so this make the porting of binaries very difficult, if not altogether impossible. It might work with some setups, and not with others. Now you have two possibilities:

  1. I assume, your 12.04 is a production environment so you cannot do whatever you want... So, in this case create an identical (virtual) machine to the Ubuntu 12.04 compile and link on it. Copy the executable to the 12.04 you need to run on it. Chances are that it will work without problems.

  2. If the assumption about the production environment is not true, then install the compilers and necessary environment and compile the source on the remote machine. This way you will know it will work all the time on that machine.

How can I link dynamically to glibc in Ubuntu

ld's default dynamic linker for i386 is /usr/lib/libc.so.1, which is wrong on most Linux systems today. You did try to override it, but the path you gave wasn't right either. Two options:

  1. Manually pass the right one when you link: ld hello.o -o hello -dynamic-linker /lib/ld-linux.so.2 -lc -m elf_i386
  2. Use gcc to link instead, as fuz mentioned: gcc -nostartfiles -m32 -o hello hello.o

If you're curious how I knew what the right dynamic linker was for option 1, I did it by doing option 2 once first and checking which one it used.

See also Red Hat's Bug 868662 - /lib/ld64.so.1: bad ELF Interpreter: No such file or directory, someone else who had basically the exact same problem you did (but they got a more helpful error message than you did for some reason).


Edit: there's two other potential problems with your code, that could cause problems in real code but just happened not to in this tiny example:

First, as Employed Russian pointed out in a comment, glibc expects that its own initialization code from its crt will run before your application code starts calling its functions. You were lucky; since you made a dynamically-linked binary, the dynamic linker's use of glibc resulted it in it being initialized for you. Had you made a statically-linked binary instead, it wouldn't have worked. To avoid relying on the dynamic linker that way, the easiest solution is to use main as your entry point instead of _start, and then use gcc -m32 -o hello hello.o to link (note we're not using -nostartfiles anymore). In theory you could still use ld directly to link, but it's complicated enough to get right that there's basically no reason to bother.

Second, you're not aligning the stack correctly. You need to make sure it's aligned to a 16-byte boundary before you call other functions. At the beginning of _start (if you still use that for some reason), the stack will already be aligned like that, so you just have to maintain it. At the beginning of main or any other function, the 4-byte return address will have gotten pushed, so you'd need to push 12 more bytes to realign it.

With both of the above fixes, here's your new hello.asm:

;hello.asm
global main

extern scanf, printf, exit

section .data
read_name db '%255s', 0
msg db 'Hello, %s', 0

section .text
main:
sub esp, 260 ; the 4 extra bytes here are padding for alignment. If you wanted to get value out of them, you could use %259s instead of %255s now
push esp
push read_name
call scanf
add esp, 8
push esp
push msg
call printf
add esp, 260 ; we pushed 268 bytes so far, but I'm leaving 8 bytes for alignment
push dword 0
call exit

Also, now that you're using main and not _start, you can just return from it instead of calling exit. You just need to make sure you put the stack pointer back where it was at the beginning. To do that, replace everything after call printf with this:

    add esp, 268
xor eax, eax
ret

Final note: if you're wondering why I did xor eax, eax and not mov eax, 0, see What is the best way to set a register to zero in x86 assembly: xor, mov or and?.



Related Topics



Leave a reply



Submit