summaryrefslogtreecommitdiff
path: root/bfd/elf.c
diff options
context:
space:
mode:
authorPedro Alves <pedro@palves.net>2022-05-11 14:20:15 +0100
committerPedro Alves <pedro@palves.net>2022-05-13 10:56:05 +0100
commit169692ce6c0fa21c4648d2862cb2bb94012a1cd9 (patch)
tree6b2032638646e5657b379d11c5b8b848bf3b4c9c /bfd/elf.c
parent31b15688c414c7caf957be63d2914faafa1b9dda (diff)
Fix "gdb --write" with core files
If you load a core file into GDB with the --write option, or "set write on" (equivalent), and then poke memory expecting it to patch the core binary, you'll notice something odd -- the write seems to succeed, but in reality, it doesn't. The value you wrote doesn't persist. Like so: $ gdb -q --write -c testsuite/outputs/gdb.base/patch/gcore.test [New LWP 615986] Core was generated by `/home/pedro/gdb/build/gdb/testsuite/outputs/gdb.base/patch/patch'. Program terminated with signal SIGTRAP, Trace/breakpoint trap. #0 0x0000555555555131 in ?? () (gdb) p *(unsigned char *)0x0000555555555131 = 1 $1 = 1 '\001' (gdb) p *(unsigned char *)0x0000555555555131 $2 = 185 '\271' (gdb) Diffing hexdumps of before/after patching, reveals that a "0x1" was actually written somewhere in the file. The problem is that the "0x1" was written at the wrong offset in the file... That happens because _bfd_elf_set_section_contents does this to seek to the section's offset: pos = hdr->sh_offset + offset; if (bfd_seek (abfd, pos, SEEK_SET) != 0 || bfd_bwrite (location, count, abfd) != count) return false; ... and 'hdr->sh_offset' is zero, so we seek to just OFFSET, which is incorrect. The reason 'hdr->sh_offset' is zero is that kernel-generated core files normally don't even have a section header table (gdb-generated ones do, but that's more an accident than a feature), and indeed elf_core_file_p doesn't even try to read sections at all: /* Core files are simply standard ELF formatted files that partition the file using the execution view of the file (program header table) rather than the linking view. In fact, there is no section header table in a core file. The process status information (including the contents of the general register set) and the floating point register set are stored in a segment of type PT_NOTE. We handcraft a couple of extra bfd sections that allow standard bfd access to the general registers (.reg) and the floating point registers (.reg2). */ bfd_cleanup elf_core_file_p (bfd *abfd) Changing _bfd_elf_set_section_contents from: pos = hdr->sh_offset + offset; to: pos = section->filepos + offset; fixes it. If we do that however, the tail end of _bfd_elf_set_section_contents ends up as a copy of _bfd_generic_set_section_contents, so just call the latter, thus eliminating some duplicate code. New GDB testcase included, which exercises both patching an executable and patching a core file. Patching an executable already works without this fix, because in that case BFD reads in the sections table. Still, we had no testcase for that yet. In fact, we have no "set write on" testcases at all, this is the first one. Tested on x86-64 GNU/Linux, gdb, ld, binutils, and gas. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=18227 Change-Id: I0f49f58b48aabab2e269f2959b8fd8a7fe36fdce
Diffstat (limited to 'bfd/elf.c')
-rw-r--r--bfd/elf.c9
1 files changed, 2 insertions, 7 deletions
diff --git a/bfd/elf.c b/bfd/elf.c
index f046994e3a..c493aa5b17 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -9404,7 +9404,6 @@ _bfd_elf_set_section_contents (bfd *abfd,
bfd_size_type count)
{
Elf_Internal_Shdr *hdr;
- file_ptr pos;
if (! abfd->output_has_begun
&& ! _bfd_elf_compute_section_file_positions (abfd, NULL))
@@ -9457,12 +9456,8 @@ _bfd_elf_set_section_contents (bfd *abfd,
return true;
}
- pos = hdr->sh_offset + offset;
- if (bfd_seek (abfd, pos, SEEK_SET) != 0
- || bfd_bwrite (location, count, abfd) != count)
- return false;
-
- return true;
+ return _bfd_generic_set_section_contents (abfd, section,
+ location, offset, count);
}
bool