Re: [PATCH v2] gdb: add support for handling core dumps on arm-none-eabi

Paul Mathieu via Gdb-patches
2020-10-06T16:59:49Z

On Tue, Oct 6, 2020 at 7:29 AM Simon Marchi <simark@simark.ca> wrote:
> On 2020-10-06 8:45 a.m., Luis Machado wrote:
> > On 10/6/20 1:32 AM, Paul Mathieu via Gdb-patches wrote:
> >> Seems to me like current_inferior()->gdbarch is returning the
> >> arm-linux gdbarch. Maybe this is because the main .elf executable
> >> itself is recognized as arm-linux? I have virtually no control over
> >> the executable image, so I would not be able to insert the same kind
> >> of note section as I would for a core file to differentiate it from
> >> arm-linux executables.
> >
> > I think there may be enough hints here and there to be able to tell
> > those apart via an heuristic. Isn't there a particular note section or
> > flag that you can fetch and tell it is not a Linux executable but a
> > bare-metal one?

I did a quick comparison between two executables.
The first one is taken from the armhf debian distribution.
The second one is for my bare-metal cortex-m4 target.

➜ bin readelf -h screen
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x6271
Start of program headers: 52 (bytes into file)
Start of section headers: 310736 (bytes into file)
Flags: 0x5000400, Version5 EABI, hard-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 28
Section header string table index: 27

➜ bin readelf -h my_bare_metal_stuff.elf
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0xc018011
Start of program headers: 52 (bytes into file)
Start of section headers: 49269716 (bytes into file)
Flags: 0x5000400, Version5 EABI, hard-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 39
Size of section headers: 40 (bytes)
Number of section headers: 76
Section header string table index: 75

The ELF header does not help us much, except that the linux executable
is marked as a DYN object, probably because it's dynamically
linked(?). I assume that statically linked executables will then use
the EXEC ELF type, which means I can't use that.
In particular, the OS/ABI section is identical. That's a quirk of the
enum, where SYSV and NONE have the same value. Yikes.

There are also some notes in the linux binary that look nice:

➜ bin readelf --notes screen

Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 76c649229b0402d6a0d1f65c3925e85fcb18e79d

Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0

My own bare-metal executable contains no notes at all.
Again, here, I'm not sure how widespread these notes are, and if linux
really requires them to run the binary or not.
As the note name suggests, it looks like a GNU extension, which
implies that it's not mandatory?

I found one more thing, trying to find differences between the 2 files.

➜ bin readelf --arch-specific screen
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "7-A"
Tag_CPU_arch: v7
Tag_CPU_arch_profile: Application
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-2
Tag_FP_arch: VFPv3-D16
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_rounding: Needed
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_align_preserved: 8-byte, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_VFP_args: VFP registers
Tag_CPU_unaligned_access: v6

➜ bin readelf --arch-specific my_bare_metal_stuff.elf
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "8-M.MAIN"
Tag_CPU_arch: v8-M.mainline
Tag_CPU_arch_profile: Microcontroller
Tag_THUMB_ISA_use: Yes
Tag_FP_arch: FPv5/FP-D16 for ARMv8
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_enum_size: small
Tag_ABI_VFP_args: VFP registers
Tag_CPU_unaligned_access: v6
Tag_DSP_extension: Allowed

The Tag_CPU_arch_profile value is different, and looks like I could use that.
Alan, would you say this tag is meaningful & reliable?

> Just so we are on the same page as to why this is happening, can you try
> to step into function "gdbarch_info_fill" while you're executable is
> loaded?
>
> What I think is happening is that gdbarch_lookup_osabi returns
> GDB_OSABI_UNKNOWN, because no sniffer identified it.
>
> So the default osabi of your build gets used instead:
>
> /* From the configured default. */
> #ifdef GDB_OSABI_DEFAULT
> if (info->osabi == GDB_OSABI_UNKNOWN)
> info->osabi = GDB_OSABI_DEFAULT;
> #endif

That looks right. `gdbarch_lookup_osabi` indeed returns GDB_OSABI_UNKNOWN.
Here's what I get in gdb after loading the main file:

(gdb) show osabi
The current OS ABI is "auto" (currently "GNU/Linux").
The default OS ABI is "GNU/Linux".

> So indeed, if there was a sniffer identifying the binary as a "none"
> osabi, it would probably work. The problem is, how do you write such a
> sniffer? It's easy to check for a Linux binary, you check for the os
> abi ELF note.

As mentioned above, I looked into ELF file differences between
arm-linux and arm-none and couldn't find anything of substance. Am I
missing something?

> But how do you identify a "none" binary? You would have
> to check that's it's not a Linux binary, not FreeBSD binary, not a
> Windows binary, etc.

Agreed. There seems to be a sniffer for PikeOS, which looks for a
specific symbol in the symbol table. There is a comment in there
saying that the BFD target is otherwise just standard ELF. I'm afraid
that if I was to use a trick, such as the Tag_CPU_arch_profile
mentioned above, I would prevent some other targets from working
properly.

> I don't really like the idea of having a "default" osabi that we fall
> back on, because it can just silently get it wrong, like in this case.
> I think it would make more sense for the fall back to be "none".
> Because it's hard to detect a "none" binary, as explained above.

I looked into configure.tgt, and it looks like default osabi is only
set for targets that have -linux in them. I'm building gdb with
`./configure --enable-targets=arm-none-eabi`, should that help? In my
case, the resulting gdb program still crashes with the same message.
Interestingly, it also prints out a warning:

warning: A handler for the OS ABI "GNU/Linux" is not built into this
configuration
of GDB. Attempting to continue with the default armv8-m.main settings.

I think in this case, the fact that it uses linux as a default osabi
could be a bug?

In the general case, a multiarch gdb would require `set osabi none` to
work, but that's already better than nothing, IMO.

> In the mean time, for your testing, you can force the osabi to none by
> using "set osabi none" prior to loading anything.

Yes, that works fine. Thanks!
I'll send a revised patch that uses GDB_OSABI_NONE shortly.

Super thanks for the great feedback!
Paul