Bug 1273250
| Summary: | Adding a new section to an ELF file SIGSEGVs and core dumps on RHEL 7 but has no issues on RHEL 6.5. The elfutils or libelf behavior are different between RHEL7 and RHEL 6.5. | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Red Hat Enterprise Linux 7 | Reporter: | Ron <monthero.ronald> | ||||
| Component: | elfutils | Assignee: | Mark Wielaard <mjw> | ||||
| Status: | CLOSED NOTABUG | QA Contact: | qe-baseos-tools-bugs | ||||
| Severity: | high | Docs Contact: | |||||
| Priority: | unspecified | ||||||
| Version: | 7.0 | CC: | adnanbajwa88, mbenitez, mjw, monthero.ronald | ||||
| Target Milestone: | rc | ||||||
| Target Release: | --- | ||||||
| Hardware: | x86_64 | ||||||
| OS: | Linux | ||||||
| Whiteboard: | |||||||
| Fixed In Version: | Doc Type: | Bug Fix | |||||
| Doc Text: | Story Points: | --- | |||||
| Clone Of: | Environment: | ||||||
| Last Closed: | 2015-10-20 12:03:33 UTC | Type: | Bug | ||||
| Regression: | --- | Mount Type: | --- | ||||
| Documentation: | --- | CRM: | |||||
| Verified Versions: | Category: | --- | |||||
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |||||
| Cloudforms Team: | --- | Target Upstream Version: | |||||
| Embargoed: | |||||||
| Attachments: |
|
||||||
Created attachment 1084583 [details]
1)My sample program to create a new section in an elf binary. Attaching my sample program eadds.c with this email )
My sample program to create a new section in an elf binary.
( Attaching my sample program eadds.c with this email )
When adding content to an existing executable ELF file you should keep the phdrs that tell where the contents of the segments are read during runtime in tact. That means either updating the phdrs after adjusting the sections (which you probably want to do anyway since you are adding an allocated sections) or (if the section you add shouldn't be allocated) you need to keep the layout of the sections exactly as they were (so the phdr offsets match).
This means you should at least use elf_flagelf (elf_ref, ELF_C_SET, ELF_F_LAYOUT); (this and the other flags should be set before you call elf_update for the fist time). In that case you are responsible for setting the section sh_offset, sh_size and sh_addr_align fields. And for updating the ehdr e_shoff field to tell libelf where the section headers should be written (normally at the end of the file).
In particular for your example you probably want to do something like:
/* Keep track of last section offset in the file. */
GElf_Off last_offset = 0;
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn (elf_ref, scn)) != NULL)
{
GElf_Shdr shdr;
if (gelf_getshdr (scn, &shdr) == NULL)
{
printf ("gelf_getshdr failed: %s\n", elf_errmsg (-1));
close (fd);
return -1;
}
if (shdr.sh_type != SHT_NOBITS
&& last_offset < shdr.sh_offset + shdr.sh_size)
last_offset = shdr.sh_offset + shdr.sh_size;
}
/* ... */
signature_scn_header->sh_offset = last_offset;
signature_scn_header->sh_size = sizeof(sample_data);
signature_scn_header->sh_addralign = 1;
ehdr->e_shoff = last_offset + sizeof(sample_data);
elf_flagelf (elf_ref, ELF_C_SET, ELF_F_LAYOUT);
/* ... */
elf_update()
Please reopen this bug if the above didn't help you and you still believe there is a bug in elfutils libelf.
Mark Thanks for the piece of info. I tried it a few times but the program core dumps (even on RHEL 6.5) when I use ELF_F_LAYOUT flag in elf_flagelf() instead of ELF_F_DIRTY flag. Probably I might be missing something when I tried to stitch the code with your code snippets ( and I am not too familiar with ELF but just just aware of a few ELF library calls to add a section in an ELF file) And also the difference in behavior between RHEL 6.5 and RHEL 7.0 concerns me a little. Even with my original sample program - It works fine on a RHEL 6.5 but sigsegv on RHEL 7 ( On RHEL 7 creation of section in ELF is successful with my initial sample program, but core dumps segv when its executed) It would be of much help if you have steps to stitch your code with my sample program so that it will enable me to readily test and report to you. I can test it on both RHEL 6.5 and RHEL 7.0 and get back. Thanks and Regards -Ron (In reply to Ron from comment #4) > I tried it a few times but the program core dumps (even on RHEL 6.5) when I > use ELF_F_LAYOUT flag in elf_flagelf() instead of ELF_F_DIRTY flag. You have to at least call elf_flagelf before the first elf_update. And if you do you are responsible for setting the the section sh_offset, sh_size and sh_addr_align fields and for updating the ehdr e_shoff field. > And also the difference in behavior between RHEL 6.5 and RHEL 7.0 > concerns me a little. Even with my original sample program - It works fine > on a RHEL 6.5 but sigsegv on RHEL 7 ( On RHEL 7 creation of section in ELF > is successful with my initial sample program, but core dumps segv when its > executed) I expect that it working on rhel 6.5 is just by accident. Note that the program you attached doesn't compile: $ gcc -g -Wall -o eadds -lelf eadds.c eadds.c: In function ‘main’: eadds.c:94:1: error: expected declaration or statement at end of input } ^ eadds.c:28:21: warning: unused variable ‘secname’ [-Wunused-variable] char *fnm, *secname = ".HP.wli"; ^ eadds.c:25:18: warning: unused variable ‘elf_scn’ [-Wunused-variable] Elf_Scn *elf_scn, *sig_scn; ^ eadds.c:24:21: warning: unused variable ‘ehdr’ [-Wunused-variable] Elf64_Ehdr *ehdr; ^ eadds.c:94:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ Thanks for the response.
Oh well I guess when u copy from the bug report the program gets mangle because of the "//" style comments and also commenting unused variables
Here is the same program with /* */ style comments (instead of //) and this should compile and execute successfully on RHEL 6.5 and fail on RHEL 7
/* PROGRAM BELOW */
/* Program to add a new section to an ELF binary
compile this program as gcc eadds.c -o eadds -lelf
Usage : ./eadds test-elf
where test-elf is your sample ELF binary or a hello world elf binary
*/
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <elf.h>
#include <libelf.h>
#include <sys/resource.h>
int main(int argc, char **argv)
{
int fd;
Elf *elf_ref = NULL;
/* Elf64_Ehdr *ehdr; */
/* Elf_Scn *elf_scn; */
Elf_Scn *sig_scn;
Elf_Data *elf_data;
Elf64_Shdr *signature_scn_header;
char *fnm;
/* char *secname = ".HP.wli"; */
unsigned char sample_data[] = {0xca, 0xfe, 0xba, 0xbe, 0xca, 0xfe, 0xba, 0xbe};
if(argc != 2){
printf("\n Usage: provide a command line input file \n");
exit(-1);
}
fnm = argv[1];
fd = open(fnm,O_RDWR, NULL);
/* Protect from using a lower ELF version and initialize ELF library */
if (elf_version(EV_CURRENT) == EV_NONE) {
printf("ELF library init failed: %s\n", elf_errmsg(-1));
close(fd);
return -1;
}
elf_ref = elf_begin(fd, ELF_C_RDWR, NULL);
if (elf_kind(elf_ref) != ELF_K_ELF) {
printf("Program is not an ELF binary\n");
close(fd);
return -2;
}
sig_scn = elf_newscn(elf_ref);
elf_data = elf_newdata(sig_scn);
elf_data->d_align = 1;
elf_data->d_off = 0LL ;
elf_data->d_buf = sample_data ;
elf_data->d_type = ELF_T_BYTE ;
elf_data->d_size = sizeof(sample_data);
elf_data->d_version = EV_CURRENT ;
signature_scn_header = elf64_getshdr(sig_scn);
if (signature_scn_header == NULL)
{
printf("ELF elf32_getshdr failed");
return -2;
}
/* signature_scn_header->sh_name = 1; */
signature_scn_header->sh_name = 16;
signature_scn_header->sh_type = SHT_NOTE;
/* signature_scn_header->sh_type = SHT_PROGBITS; */
signature_scn_header->sh_flags = SHF_ALLOC;
signature_scn_header->sh_entsize = 0;
if (elf_update(elf_ref , ELF_C_NULL ) < 0) {
printf("ELF update failed: %s", elf_errmsg (-1));
return -3;
}
(void) elf_flagshdr(sig_scn, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagscn(sig_scn, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagdata(elf_data, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagehdr(elf_ref, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagelf(elf_ref, ELF_C_SET, ELF_F_DIRTY);
if (elf_update(elf_ref , ELF_C_WRITE ) < 0) {
printf("ELF update failed: %s", elf_errmsg (-1));
return -4;
}
elf_end(elf_ref);
close(fd);
return 0;
}
readelf -S testx
..
[28] .symtab SYMTAB 0000000000000000 000009e0
0000000000000600 0000000000000018 29 46 8
[29] .strtab STRTAB 0000000000000000 00000fe0
00000000000001f0 0000000000000000 0 0 1
[30] NULL 0000000000000000 1000300000000
0000000000400200 000000000040021c 0 0 562962838323200
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
[root@srp-linux-7-rhel6 wli-elf]#
Section 30 is the new section added.
Successful program Execution after adding a new section to the elf binary.
[root]# ./testx
Hello Linux Users
[root@srp-linux-7-rhel6 wli-elf]#
Thanks for the response.
Oh well I guess when u copy from the bug report the program gets mangle because of the "//" style comments and also commenting unused variables
Here is the same program with /* */ style comments (instead of //) and this should compile and execute successfully on RHEL 6.5 and fail on RHEL 7
/* PROGRAM BELOW */
/* Program to add a new section to an ELF binary
compile this program as gcc eadds.c -o eadds -lelf
Usage : ./eadds test-elf
where test-elf is your sample ELF binary or a hello world elf binary
*/
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <elf.h>
#include <libelf.h>
#include <sys/resource.h>
int main(int argc, char **argv)
{
int fd;
Elf *elf_ref = NULL;
/* Elf64_Ehdr *ehdr; */
/* Elf_Scn *elf_scn; */
Elf_Scn *sig_scn;
Elf_Data *elf_data;
Elf64_Shdr *signature_scn_header;
char *fnm;
/* char *secname = ".HP.wli"; */
unsigned char sample_data[] = {0xca, 0xfe, 0xba, 0xbe, 0xca, 0xfe, 0xba, 0xbe};
if(argc != 2){
printf("\n Usage: provide a command line input file \n");
exit(-1);
}
fnm = argv[1];
fd = open(fnm,O_RDWR, NULL);
/* Protect from using a lower ELF version and initialize ELF library */
if (elf_version(EV_CURRENT) == EV_NONE) {
printf("ELF library init failed: %s\n", elf_errmsg(-1));
close(fd);
return -1;
}
elf_ref = elf_begin(fd, ELF_C_RDWR, NULL);
if (elf_kind(elf_ref) != ELF_K_ELF) {
printf("Program is not an ELF binary\n");
close(fd);
return -2;
}
sig_scn = elf_newscn(elf_ref);
elf_data = elf_newdata(sig_scn);
elf_data->d_align = 1;
elf_data->d_off = 0LL ;
elf_data->d_buf = sample_data ;
elf_data->d_type = ELF_T_BYTE ;
elf_data->d_size = sizeof(sample_data);
elf_data->d_version = EV_CURRENT ;
signature_scn_header = elf64_getshdr(sig_scn);
if (signature_scn_header == NULL)
{
printf("ELF elf32_getshdr failed");
return -2;
}
/* signature_scn_header->sh_name = 1; */
signature_scn_header->sh_name = 16;
signature_scn_header->sh_type = SHT_NOTE;
/* signature_scn_header->sh_type = SHT_PROGBITS; */
signature_scn_header->sh_flags = SHF_ALLOC;
signature_scn_header->sh_entsize = 0;
if (elf_update(elf_ref , ELF_C_NULL ) < 0) {
printf("ELF update failed: %s", elf_errmsg (-1));
return -3;
}
(void) elf_flagshdr(sig_scn, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagscn(sig_scn, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagdata(elf_data, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagehdr(elf_ref, ELF_C_SET, ELF_F_DIRTY);
(void) elf_flagelf(elf_ref, ELF_C_SET, ELF_F_DIRTY);
if (elf_update(elf_ref , ELF_C_WRITE ) < 0) {
printf("ELF update failed: %s", elf_errmsg (-1));
return -4;
}
elf_end(elf_ref);
close(fd);
return 0;
}
readelf -S testx
..
[28] .symtab SYMTAB 0000000000000000 000009e0
0000000000000600 0000000000000018 29 46 8
[29] .strtab STRTAB 0000000000000000 00000fe0
00000000000001f0 0000000000000000 0 0 1
[30] NULL 0000000000000000 1000300000000
0000000000400200 000000000040021c 0 0 562962838323200
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
[root@srp-linux-7-rhel6 wli-elf]#
Section 30 is the new section added.
Successful program Execution after adding a new section to the elf binary.
[root]# ./testx
Hello Linux Users
[root@srp-linux-7-rhel6 wli-elf]#
(In reply to Ron from comment #1) > Created attachment 1084583 [details] > 1)My sample program to create a new section in an elf binary. Attaching my > sample program eadds.c with this email ) > > My sample program to create a new section in an elf binary. > ( Attaching my sample program eadds.c with this email i compile your program according top your instruction but at the end i received "program is not in elf binary" |
Description of problem: Architecture: x86_64 I am using libelf APIs to add a new section to an ELF binary. The libelf behavior seems to exhibit different behavior between RHEL 6.5 and RHEL 7.0 I have a sample program that I compiled to add a new section to an ELF file which I used a reference on both systems. It works fine on a RHEL 6.5 system and but core dumps on RHEL 7 with a sigsegv and after the arch_prctl( ) on a RHEL 7.0 system The new section in the ELF gets created even on RHEL 7 but it sigsegvs and dumps core. The only difference is between the versions of libelf and binutils used. I encounter this failure, even with the latest version of elfutils and binutils available for RHEL 7 Version-Release number of selected component (if applicable): oN RHEL 7.0 ## rpm -qa | grep -i elfutils elfutils-libelf-devel-0.160-1.el7.x86_64 elfutils-libelf-0.160-1.el7.x86_64 elfutils-0.160-1.el7.x86_64 elfutils-libs-0.160-1.el7.x86_64 ## ## rpm -qa | grep -i binutils binutils-devel-2.23.52.0.1-30.el7.x86_64 binutils-2.23.52.0.1-30.el7.x86_64 ## ON RHEL 6.5 (Runs Successful ) ## rpm -qa | grep -i elfutils elfutils-devel-0.161-3.el6.x86_64 elfutils-libelf-0.161-3.el6.x86_64 elfutils-libs-0.161-3.el6.x86_64 elfutils-0.161-3.el6.x86_64 elfutils-libelf-devel-0.161-3.el6.x86_64 ## ## rpm -qa | grep -i binutils binutils-devel-2.20.51.0.2-5.36.el6.x86_64 binutils-2.20.51.0.2-5.36.el6.x86_64 ## How reproducible: Steps to Reproduce: Procedure to reproduce: 1) A sample program to create a new section in an elf binary. ( Attaching my sample program eadds.c with this email ) Compile this program gcc eadds.c –o eadds –lelf 2) Compile a test elf binary of a sample hello world program. Use readelf –S hello-world ( To know the number of sections in the ELF binary before adding a new section) 3) Create a new section in this hello world binary and verify the same using readelf utility ./eadds hello-world readelf –S hello-world ( Usually the last section added is the new section ) 4) Now run the hello-world binary – Expected Output: It should run successfully and display the text (Hello WOrld) though its embedded with a new section in the elf. Actual results: [root@localhost test-elf]# strace ./a.out execve("./a.out", ["./a.out"], [/* 31 vars */]) = 0 brk(0) = 0x699000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2daf534000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2daf533000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2daf532000 arch_prctl(ARCH_SET_FS, 0x7f2daf533680) = 0 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x8} --- +++ killed by SIGSEGV (core dumped) +++ Segmentation fault (core dumped) [root@localhost test-elf]# Expected results: After running ./eadds hello-world It should add a new section to the ELF you can valiadte it using readelf -S hello-world. Typically the new section gets added at the end towards the last section. And when you run the modified hello-world ELF binary it should execute as usual and display the text. Example: [root@rhel65 wli-elf-temp]# readelf -S a.out There are 31 section headers, starting at offset 0x11d8: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400200 00000200 000000000000001c 0000000000000000 A 0 0 1 ... ... ... ... [29] .strtab STRTAB 0000000000000000 00000fe0 00000000000001f0 0000000000000000 0 0 1 [30] ab NOTE 0000000000000000 000011d0 0000000000000008 0000000000000000 A 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) [root@rhel65 wli-elf-temp]# In the above successful case (RHEL 6.5), the new section created is section 30. Run the modified ELF: [root@rhel65 wli-elf-temp]# ./a.out Hello Linux Users !! [root@rhel65 wli-elf-temp]# Additional info: The program to add anew section is attached in this report and is named eadds.c. But the hello world sample program needs to be created, you can create a sample hello world program as the ELF to be modified by eadds