nm for MIPS32 shows addresses from 0x8000000 and above as sign-extended(!) 64bit addresses. To reproduce: $ cat <<-eof >test.c int test(void) { return 0; } eof $ echo ". = 0x80000000;" >test.lds $ mips-linux-gnu-gcc test.c -Wl,-T,test.lds -static -nostdlib -ffreestanding -o test $ mips-linux-gnu-nm test ffffffff80000000 T test $ x86_64-gnu-linux-nm test 80000000 T test Tested with x86_64-cross-mips32-nm (binutils 2.33, 2.40 (alpine); 2.35, 2.41 (Debian)) as well as with native nm (under qemu-mips-chroot). (Same behaviour was also observed with binutils-multiarch (Debian).) Initially, the failure was observed during compilation of Linux for MIPS32, where mips32-nm produces a System.map with sign-extended 64bit addresses. Native x86_64-nm shows correct 32bit addresses on the same binary files.
Hi Nicolas, This is actually expected behaviour. The MIPS architecture uses a signed address space, and tools configured to support the MIPS are aware of this. Hence the mips-linux-gnu-nm tool displays a signed address. Some tools however can be used on architectures for which they were not configured. Nm is one of these tools. So if you use x86_64-linux-gnu-nm on a MIPS binary, it will be able to display the symbols, but it will display their addresses as unsigned values, since that is how the x86_64 architecture treats its addresses. Basically if you want to discover information about MIPS binaries, use tools that are configured to support the MIPS architecture. Note - it is still possible to use cross tools, they just need to support the MIPS. So for example if you build the binutils on an x86_64 host and use the "--enable-targets=all" configure option, then they will be able to correctly handle MIPS binaries, as well as x86_64 binaries and indeed most other architectures as well. Does this make sense ? Cheers Nick
Hi Nick, (In reply to Nick Clifton from comment #1) > Hi Nicolas, > > This is actually expected behaviour. > > The MIPS architecture uses a signed address space, and tools configured to > support the MIPS are aware of this. Hence the mips-linux-gnu-nm tool > displays a signed address. Some tools however can be used on architectures > for which they were not configured. Nm is one of these tools. So if you > use x86_64-linux-gnu-nm on a MIPS binary, it will be able to display the > symbols, but it will display their addresses as unsigned values, since that > is how the x86_64 architecture treats its addresses. > > Basically if you want to discover information about MIPS binaries, use > tools that are configured to support the MIPS architecture. thanks for the insights and explanations, I am still a bit bewildered of "signed address space" and its implications. The most irritating thing for me is still that a mip32-native `nm` shows addresses of a mips32-native binary as 64-bit addresses, while mips32-native `objdump` shows 32-bit addresses: $ objdump -t ../my-mips32-binary ../my-mips32-binary: file format elf32-tradlittlemips SYMBOL TABLE: 80000000 l d .note.gnu.build-id 00000000 .note.gnu.build-id 80000030 l d .text 00000000 .text [...] 00000000 l df *ABS* 00000000 test.c 80000030 g F .text 00000024 test $ nm ../my-mips32-binary ffffffff80000030 T test For my local work, a simple patch such as: diff --git a/binutils/nm.c b/binutils/nm.c index e4c8036df1b..7e42ce8f469 100644 --- a/binutils/nm.c +++ b/binutils/nm.c @@ -1821,6 +1821,9 @@ print_value (bfd *abfd ATTRIBUTE_UNUSED, bfd_vma val) switch (print_width) { case 32: + printf (print_format_string, 0xffffffff & (uint64_t) val); + break; + case 64: printf (print_format_string, (uint64_t) val); break; would be sufficient to get the output that I expect: $ binutils/nm-new ../mips32-test 80000030 T test here, `nm-new` is a mips32-native `nm`. A native mips64el build of `nm` shows this: $ binutils/nm-new /tmp/mips64el-test 0000000080000030 T test $ binutils/nm-new /tmp/mips32-test 80000030 T test which is exactly what I had originally expected. Am I still missing some point? Is it really intended that `nm` shows 64-bit addresses for mips32 targets? If not, I plan to forward the patch to binutils@sourceware.org. Kind regards, Nicolas
(In reply to Nicolas Schier from comment #2) > thanks for the insights and explanations, I am still a bit bewildered of > "signed address space" and its implications. Agreed. I have always felt that it was a strange choice by the MIPS designers. > Am I still missing some point? Is it really intended that `nm` shows 64-bit > addresses for mips32 targets? No - you are right, this is a bug. > If not, I plan to forward the patch to binutils@sourceware.org. Please do. Cheers Nick
Created attachment 15234 [details] nm: Enforce 32-bit width limit when printing 32-bit values Patch is sent to binutils@sourceware.org: https://sourceware.org/pipermail/binutils/2023-December/130966.html
The master branch has been updated by Alan Modra <amodra@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=3c8852fcc806915fdeab8b3d6f49be7347160527 commit 3c8852fcc806915fdeab8b3d6f49be7347160527 Author: Alan Modra <amodra@gmail.com> Date: Tue Dec 5 09:23:41 2023 +1030 PR31096, nm shows 32bit addresses as 64bit addresses Prior to commit 0e3c1eebb2 nm output depended on the host unsigned long when printing "negative" symbol values for 32-bit targets. Commit 0e3c1eebb22 made the output match that seen with a 64-bit host unsigned long. The fact that nm output changed depending on host is of course a bug, but it is reasonable to expect 32-bit target output is only 32 bits. So this patch makes 32-bit target output the same as it was on 32-bit hosts prior to 0e3c1eebb2. PR 31096 * nm.c (print_format_string): Make it a static buffer. (get_print_format): Merge into.. (set_print_format): ..this, renamed from set_print_width. When print_width is 32, set up print_format_string for an int32_t value. Don't malloc print_format_string. Adjust calls. (print_value): Correct printing of 32-bit values.
Fixed