ldd -r equivalent on macOS - macos

I am trying to make my software available on macOS and in my toolchain I use ldd -r MyModel.so to verify is everything went well but I can't really find anything conclusive on macOS that would have the same behaviour.
otool or nm seems the two directions to go but I am not sure how to be sure what options would behave the same. Or is there another tool ?

ldd uses ld to load executable files, and recursively loads
dynamically-linked libraries. So using ldd requires being on the target system
(e.g., Linux). Thus, ldd cannot be used for ELF files on macOS.
Assuming that the question is about analyzing Mach-O files on macOS, I do not know of any tool that works for Mach-O files as ldd does for ELF files.
Both otool and nm perform a static analysis.
A possibility is:
otool -L /usr/bin/true
Relevant:
MacOSX: which dynamic libraries linked by binary?
Inspect and get binary from ELF file on MAC

Related

Cross compiled binary not running on RPI, did I compile it correctly?

I am trying to cross compile a small rust application for the RPI. I am cross compiling because compiling directly on the PI takes way too long and it hits 75C.
I followed various instructions, but what I ended up doing is this:
Install "armv7-unknown-linux-gnueabihf" target with rustup
Download rpi tools from here: https://github.com/raspberrypi/tools
Add the "tools/arm-bcm2708/arm-linux-gnueabihf/bin/" folder to PATH
Add ".cargo/config" file with:
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
run "cargo build --target armv7-unknown-linux-gnueabihf --release"
scp the file to the RPI
chmod +x the_file
do "./the_file"
I get bash: ./the_file: No such file or directory
Yes, I am indeed in the right directory.
So this is the output from "file":
ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically
linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.32,
with debug_info, not stripped
I'm not experienced enough with this sort of stuff to determine if the binary that I produced is suitable to be run on an RPI3 B.
Did I produce the correct "type" of binary?
P.S. I am running DietPi distro on the PI. It is based on debian if that's of any relevance.
So I solved this by cheating. I found https://github.com/rust-embedded/cross which took about 30 seconds to get going and now I can cross compile to pretty much anything. I highly recommend it!
The error message "No such file or directory" is not about the your executable but about the dynamic libraries linked to it which are missing from the target system.
To find out which libraries your executable needs you have to run the following command.
ldd /usr/bin/lsmem
This will output something like this
linux-vdso.so.1 (0x00007fffc87f1000)
libsmartcols.so.1 => /lib/x86_64-linux-gnu/libsmartcols.so.1 (0x00007fe82fe71000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe82fc7f000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe82fedd000)
Now you have to check that all this libraries are available on your system. rust-cross probably uses the correct linker for your target so that is probably the reason this works with it. To modify the linker see https://stackoverflow.com/a/57817848/5809980

Inspect and get binary from ELF file on MAC

I have a ELF file and I want to get a hex or bin file of my code from it. In the terminal, if a do a file main, which is my file, it shows:
main: ELF 32-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, not stripped
As I learned, objdump on MAC is not working and with otool -l main I get the following error:
llvm-objdump: 'main': Object is not a Mach-O file type.
The ELF file was created using the command:
riscv-none-gcc/8.2.0-2.1-20190425-1021/bin/riscv-none-embed-gcc --specs=nosys.specs main.c -o main
So is there a way to do it?
Thanks a lot
For creating "raw binary files"
the program objcopy
can be used, as described here:
objcopy -O binary foo.elf foo.bin
The program objcopy is part of the MacPorts
package x86_64-elf-binutils,
and can be used as follows:
/opt/local/bin/x86_64-elf-objcopy -O binary foo.elf foo.bin
where foo.elf is an ELF file compiled on (or cross-compiled for) an x86_64
Linux. The MacPorts package x86_64-elf-binutils can be installed as follows:
port install x86_64-elf-binutils
The program objcopy is part of binutils.
For Mach-O, it can be installed on macOS via the package binutils
of MacPorts, as follows:
port install binutils
The MacPorts binutils package installs gobjcopy.
Versions of binutils on macOS for cross-development for other target systems,
too, are available
via MacPorts.
This post is motivated also by MacOSX: which dynamic libraries linked by binary?,
and is intended to be also informational.
Executables can be:
ELF on Linux
Mach-O on macOS
ldd
ldd is a script in Linux
that wraps ld. It is described as
print shared object dependencies
The GNU ld is unavailable on macOS. More fundamentally, that ldd calls ld
means that its operation is non-static, in contrast to tools like readelf,
objdump, and nm.
In that sense, even if certain information is obtainable using tools other
than ldd, the results are not equivalent, because the other tools do not
attempt to load the binary. Moreover, attempting to load a binary requires
being on a Linux, so ldd is
genuinely a Linux tool that cannot be emulated exactly by a program on macOS.
A relevant description.
There does exist a pure-Python implementation that approximates ldd without
loading binaries the way that ld does: lddcollect.
Using lddcollect is possible on a Linux system, where the required libraries
are present.
One reason to not use ldd is security: inspecting executables without
executing them.
ldd is an initialism for
"List Dynamic Dependencies".
ldd appears to be a bash script that is part of glibc, with source code at:
https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/ldd.bash.in;h=ba736464ac5e4a9390b1b6a39595035238250232;hb=271ec55d0ae795f03d92e3aa61bff69a31a19e3a
Relevant: What is the difference between ldd and objdump?
Relevant: cross compiler ldd
objdump
objdump shows information about
object files, and can disassemble them. It is part of binutils.
Programs that are called objdump on macOS:
/opt/local/bin/gobjdump by the MacPorts package binutils
/usr/bin/objdump by macOS (part of package com.apple.pkg.Essentials),
which is described as the
llvm object file dumper
The manual of ldd suggests calling objdump as an alternative, as follows:
objdump -p /path/to/program | grep NEEDED
Relevant: https://superuser.com/questions/206547/how-can-i-install-objdump-on-mac-os-x
readelf
readelf displays information about
ELF files by reading them (static, not loading them). It is part of binutils.
It does not disassemble files, like objdump can.
Variants available on macOS:
/opt/local/bin/greadelf from the MacPorts package binutils
/opt/local/bin/elftc-readelf from the MacPorts package elftoolchain
Example usage:
readelf -s elf_file
nm
/usr/bin/nm by macOS (part of package com.apple.pkg.Essentials)
/opt/local/bin/nm by the MacPorts package cctools,
which is a symbolic link: /opt/local/bin/nm -> llvm-nm-mp-10
/opt/local/bin/nm-classic by the MacPorts package cctools
/opt/local/bin/elftc-nm by the MacPorts package elftoolchain
/opt/local/bin/gnm by the MacPorts package binutils
Apparently, both /usr/bin/nm and /opt/local/bin/nm are versions of the
llvm symbol table dumper
and do work with ELF files.
otool (and variants)
otool is the the disassembler for MacOS's Mach-O format.
Variants of otool available on macOS:
/usr/bin/otool by macOS (part of package com.apple.pkg.Essentials)
/opt/local/bin/otool by the MacPorts package cctools,
which links to /opt/local/bin/llvm-otool by the MacPorts package cctools,
which is described as:
the otool-compatible command line parser for llvm-objdump
/opt/local/bin/otool-classic by the MacPorts package cctools
More details:
> which -a otool
/opt/local/bin/otool
/usr/bin/otool
> ls -lsa /opt/local/bin/otool
... /opt/local/bin/otool -> llvm-otool
> port provides /opt/local/bin/otool
/opt/local/bin/otool is provided by: cctools
> which -a llvm-otool
/opt/local/bin/llvm-otool
> port provides /opt/local/bin/llvm-otool
/opt/local/bin/llvm-otool is provided by: cctools
> ls -lsa /usr/bin/otool
... /usr/bin/otool
> pkgutil --file-info /usr/bin/otool
volume: /
path: /usr/bin/otool
pkgid: com.apple.pkg.Essentials
...
The MacPorts package cctools installs also /opt/local/bin/otool-classic,
which, as said in its documentation, is obsolete.
elfdump
elfdump is available on macOS via the MacPorts package elftoolchain,
and installed as the binary /opt/local/bin/elftc-elfdump.
strings
The program strings can be
useful for inspecting the symbols contained in an ELF file. It is a more
general tool, not designed specifically for ELF files, but usable nonetheless.
Variants of strings on macOS:
/usr/bin/strings by macOS (part of package com.apple.pkg.Essentials)
/opt/local/bin/strings from the MacPorts package cctools
/opt/local/bin/elftc-strings from the MacPorts package elftoolchain
/opt/local/bin/gstrings from the MacPorts package binutils
Example usage (piping to ag):
strings some_elf_file | ag GLIBC
elftc-strings appears to have fewer options and give fewer results than
the other strings implementations (which differ with each other, but seem
to print similar results).
elftoolchain
Available via MacPorts, elftoolchain
is a BSD-licensed library of tools like those in binutils. Tools from that
collection that are relevant to analyzing ELF files:
/opt/local/bin/elftc-elfdump
/opt/local/bin/elftc-nm
/opt/local/bin/elftc-readelf
/opt/local/bin/elftc-strings
There are also plans for
implementing objdump.
Confirming that a binary is from MacPorts
To find out whether a given file is part of MacPorts:
> port provides /opt/local/bin/otool
/opt/local/bin/otool is provided by: cctools
Discussed in this answer.
Confirming that a binary is from macOS
Also useful for finding out how each
of the tools discussed above was installed is pkgutil:
pkgutil --file-info /usr/bin/objdump
This can be useful to confirm that a binary was part of macOS itself, and not
installed by other means.
Confirming that an executable is an ELF
This text was motivated when I wanted to analyze an executable with the
following details:
> file filename
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, not stripped
As for other tools, there are multiple options for file on macOS:
/usr/bin/file by macOS (part of package com.apple.pkg.Essentials)
/opt/local/bin/file by the MacPorts package file
Other tools
Apparently, on some operating systems there are also elftools available.
For analyzing files for specific architectures, there are MacPorts packages
like arm-elf-binutils.
DWARF
There is also DWARF and dwarftool,
as well as dwarfdump (part of XCode).
Miscellanea
binutils can analyze Mach-O on Linux: https://stackoverflow.com/a/8714142/1959808
darling can be used on Linux to run
macOS software (Mach-O, dyld), it is analogous to wine

How do you debug libc in OSX?

I would expect to be able to statically link against a build of libc with debug symbols, then run my program through lldb.
otool -L <my binary>
makes it look like the only dynamically linked library (dll) is:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
Which I would guess is the libc dll is (though nm doesn't list everything I'd expect in libc). Maybe just some kind of stub loader.
In /usr/lib/ it seems that Apple does not supply a debug build of libSystem.
How do you debug libc on OSX (10.11.3)?
Possible duplicate of:
On Mac OS X, how can you get a debug build of System/LibC for source level debugging?
I'm having a hard time following your question, but there are certain points to be made which I think will cover what you're asking:
otool -L will show all dylibs that you are linking against.
/usr/lib/libSystem.B.dylib is an umbrella library that re-exports multiple dylibs, including libc (/usr/lib/system/libsystem_c.dylib). If you want to see what symbols are part of the system's C runtime, you will want to use nm on that dylib, not libSystem.
If you are statically linking in your own libc, then it won't show up in 'otool -L' because it's a static library and is thus part of your executable.
If you staticy linked your own libc into your executable, then using nm on your binary will possibly not show all the symbols you are expecting because the linker will optimize away dead code that is not needed by your executable. You can ensure that everything is added by using the -force_load option to ld. Check out the ld man page for more details on that.

How to obtain readelf and objdump binaries for OS X?

From where can I download readelf and objdump binaries for OS X? I'm trying to get the list of exported functions from an NDK .so library and neither nm nor otool worked for me. I've read that the library might be in elf format and that readelf or objdump might work.
I was able to find the source code for those utilities but I would like the binaries. Surely they've been compiled by someone already.
There was a lot of information in this SO article: How do I list the symbols in a .so file It is there that readelf and objdump are recommended when nm did not work for me.
These tools are available as part of the NDK. You'll find them in the toolchains subdirectory within the NDK, e.g. android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objdump. There's also a version of the nm utility there which will understand your ELF .so files, arm-linux-androideabi-nm in the same path as above.

GCC linking to a shared object's linker name

Suppose I have:
/usr/lib/libsomething.so.1 on machine A;
/usr/lib/libsomething.so.2 on machine B.
Both machines have /usr/lib/libsomething.so symlinking to their respective libs.
If I link using gcc with -lsomething (or even /usr/lib/libsomething.so) it will follow the symlink, and ldd on machine A produces something like:
libsomething.so.1 => /usr/lib/libsomething.so.1
This means it won't be able to find the library on machine B.
Now I know these are major version number changes and I know they may not be compatible, but I'm willing to take that risk. What I'd like to tell the linker is to look for libsomething.so, and don't follow the symlink so ldd will show
libsomething.so => /usr/lib/libsomething.so.1
on A but
libsomething.so => /usr/lib/libsomething.so.2
on B. And then the loader will follow the symlink to whatever version is there.
Also, I don't want delayed loading with dlopen or anything. I want it to link to the shared object at compile time.
Is this even possible?
Making executable which uses any available version of shared library is, of course, possible.
The problem was that you linked your executable to version-specific soname (libsomething.so.1 and libsomething.so.2). You should have done it with unversioned soname libsomething.so instead.
In order to achieve this, on the build machine you should compile and install library with soname (ELF SONAME) equal to libsomething.so (without version) so that linker can choose this soname while executable is built.
According to the Shared Libraries HOWTO, you can pass required unversioned soname while building the library:
gcc -shared -Wl,-soname,libsomething.so -o libsomething.so.X objectsomething.o
Then, as soon as you install the library and run ldconfig, you have:
symlink /lib/libsomething.so pointing to /lib/libsomething.so.1 on machine A;
symlink /lib/libsomething.so pointing to /lib/libsomething.so.2 on machine B.
The loader (run ldd) will choose unversioned symlinks regardless where it points to:
libsomething.so => /lib/libsomething.so (0xNNNNNNNN) on machine A;
libsomething.so => /lib/libsomething.so (0xNNNNNNNN) on machine B.
Linux dynamic loader (ld.so) resolves libraries based on their soname value written in the executable (ELF NEEDED). The value is copied from library file (ELF SONAME) while building the executable. As long as there is a symlink on the target system matching the soname recorded in the executable, the library pointed by this symlink will be loaded.
Let's run through your setup and show commands to verifing assumptions.
I used Fedora 18 X86_64 for the test and adjusted output to i686 for clarity.
Compile both libsomething.so.1 and libsomething.so.2. Make sure SONAME is set to unversioned libsomething.so:
readelf -a libsomething.so.1 | grep SONAME
0xNNNNNNNN (SONAME) Library soname: [libsomething.so]
readelf -a libsomething.so.2 | grep SONAME
0xNNNNNNNN (SONAME) Library soname: [libsomething.so]
Install the libraries into their respective machines under /lib/ directory. Run ldconfig -v on both machines and verify the output.
ldconfig -v 2>&1 | grep something
libsomething.so -> libsomething.so.1 (changed)
ldconfig -v 2>&1 | grep something
libsomething.so -> libsomething.so.2 (changed)
Compile executable and make sure that it refers to the same soname without version in NEEDED.
readelf -a executable | grep NEEDED
0xNNNNNNNN (NEEDED) Shared library: [libsomething.so]
You executable depends on unversioned libsomething.so now. Copy executable to both machines and run ldd against both copies.
ldd executable
libsomething.so => /lib/libsomething.so (0xNNNNNNNN)
The last output is the same on both machines as the executable was built with soname without version. This makes loader take unversioned symlinks on targets machines. And depending on the machine, the symlink can point to different implementation of the library libsomething.so.1 or libsomething.so.2.
This means it won't be able to find the library on machine B.
And it's not supposed to anyway.
By the very definition of soversions, libsomething.so.2 denotes that the API/ABI is incompatible to libsomething.so.1. Therefore, just adding libsomething.so in the program's table of libraries to be loaded would be factually wrong. The libsomething.so symlink merely serves as a hint to ld as to which soversion to pick by default.
Of whatever file ld actually ended up opening, it will take the DTNAME/SONAME field to encode in the program. If you don't want that, don't equip libsomething with a soname. But it can easily become pain... starting with running into unavailable symbols when trying to run the program.

Resources