[devel] verify-elf lint=normal (elflint)

Alexey Tourbin at на altlinux.ru
Чт Дек 24 04:33:04 UTC 2009


On Thu, Dec 24, 2009 at 03:57:22AM +0300, Alexey Tourbin wrote:
> On Mon, Dec 21, 2009 at 11:08:52PM +0300, Dmitry V. Levin wrote:
> > glibc-6:2.10.1-alt8
> > loadable segment [1] is writable but contains no writable sections
> > verify-elf: WARNING: ./sbin/glibc_preinstall: eu-elflint failed
> 
> > klibc-1.5.15-alt4
> > loadable segment [3] is writable but contains no writable sections
> > verify-elf: WARNING: ./lib/mkinitrd/klibc/bin/cat: eu-elflint failed
> > [9 lines skipped]
> > verify-elf: WARNING: ./lib/mkinitrd/klibc/bin/umount: eu-elflint failed
> > loadable segment [3] is writable but contains no writable sections
> > verify-elf: WARNING: ./usr/lib/klibc/bin/cat: eu-elflint failed
> > [12 lines skipped]
> > section [ 4] '.interp' present in object file
> > section [ 4] '.interp' has SHF_ALLOC flag set but there is no loadable segment
> > verify-elf: WARNING: ./usr/lib/klibc/lib/interp.o: eu-elflint failed
> 
> > util-linux-2.16.1-alt3
> > loadable segment [1] is writable but contains no writable sections
> > verify-elf: WARNING: ./sbin/nologin: eu-elflint failed
> > verify-elf: WARNING: ./usr/bin/pause: eu-elflint failed
> 
> It looks like all klibc executables (compiled with klcc) are subject
> to this warning.  Let's try to fix it before we discuss whether
> lint=normal should be on by default.

Let's test a simple program to examine the problem.

$ cat test.c
int foo = 0;
int main()
{
        return 0;
}
$ klcc test.c && readelf --segments a.out && eu-elflint --gnu-ld a.out
Elf file type is EXEC (Executable file)
Entry point 0x4000e8
There are 3 program headers, starting at offset 64
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000001ac 0x00000000000001ac  R E    200000
  LOAD           0x00000000000001b0 0x00000000006001b0 0x00000000006001b0
                 0x0000000000000000 0x0000000000000030  RW     200000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
 Section to Segment mapping:
  Segment Sections...
   00     .text 
   01     .bss 
   02     
loadable segment [1] is writable but contains no writable sections
$ 

The last line, which is eu-elflint output, suggests there's a problem.
Now, if we replace "foo = 0" with "foo = 1", the test will pass.

$ cat test.c
int foo = 1;
int main()
{
        return 0;
}
$ klcc test.c && readelf --segments a.out && eu-elflint --gnu-ld a.out
Elf file type is EXEC (Executable file)
Entry point 0x4000e8
There are 3 program headers, starting at offset 64
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000001ac 0x00000000000001ac  R E    200000
  LOAD           0x00000000000001ac 0x00000000006001ac 0x00000000006001ac
                 0x0000000000000004 0x000000000000002c  RW     200000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    8

 Section to Segment mapping:
  Segment Sections...
   00     .text 
   01     .data .bss 
   02     
No errors
$

(The last line now says "No errors".)

So the problem is that, while ".data" section contributes to the ELF
segment being writable, ".bss" section alone does not.

Let's see the code.

elfutils-0.143/src/elflint.c:
  3694        if (ehdr->e_type != ET_REL && (shdr->sh_flags & SHF_ALLOC) != 0)
  3695          {
  3696            /* Make sure the section is contained in a loaded segment
  3697               and that the initialization part matches NOBITS sections.  */
  3698            int pcnt;
  3699            GElf_Phdr phdr_mem;
  3700            GElf_Phdr *phdr;
  3701  
  3702            for (pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt)
  3703              if ((phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem)) != NULL
  3704                  && ((phdr->p_type == PT_LOAD
  3705                       && (shdr->sh_flags & SHF_TLS) == 0)
  3706                      || (phdr->p_type == PT_TLS
  3707                          && (shdr->sh_flags & SHF_TLS) != 0))
  3708                  && phdr->p_offset <= shdr->sh_offset
  3709                  && (phdr->p_offset + phdr->p_filesz > shdr->sh_offset
  3710                      || (phdr->p_offset + phdr->p_memsz > shdr->sh_offset
  3711                          && shdr->sh_type == SHT_NOBITS)))
  3712                {
  3713                  /* Found the segment.  */
  3714                  if (phdr->p_offset + phdr->p_memsz
  3715                      < shdr->sh_offset + shdr->sh_size)
  3716                    ERROR (gettext ("\
  3717  section [%2zu] '%s' not fully contained in segment of program header entry %d\n"),
  3718                           cnt, section_name (ebl, cnt), pcnt);
  3719  
  3720                  if (shdr->sh_type == SHT_NOBITS)
  3721                    {
  3722                      if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz
  3723                          && !is_debuginfo)
  3724                        ERROR (gettext ("\
  3725  section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d\n"),
  3726                           cnt, section_name (ebl, cnt), pcnt);
  3727                    }
  3728                  else
  3729                    {
  3730                      const GElf_Off end = phdr->p_offset + phdr->p_filesz;
  3731                      if (shdr->sh_offset > end ||
  3732                          (shdr->sh_offset == end && shdr->sh_size != 0))
  3733                        ERROR (gettext ("\
  3734  section [%2zu] '%s' has not type NOBITS but is not read from the file in segment of program header entry %d\n"),
  3735                           cnt, section_name (ebl, cnt), pcnt);
  3736                    }
  3737  
  3738                  if (shdr->sh_type != SHT_NOBITS)
  3739                    {
  3740                      if ((shdr->sh_flags & SHF_EXECINSTR) != 0)
  3741                        {
  3742                          segment_flags[pcnt] |= PF_X;
  3743                          if ((phdr->p_flags & PF_X) == 0)
  3744                            ERROR (gettext ("\
  3745  section [%2zu] '%s' is executable in nonexecutable segment %d\n"),
  3746                                   cnt, section_name (ebl, cnt), pcnt);
  3747                        }
  3748  
  3749                      if ((shdr->sh_flags & SHF_WRITE) != 0)
  3750                        {
  3751                          segment_flags[pcnt] |= PF_W;
  3752                          if (0   /* XXX vdso images have this */
  3753                              && (phdr->p_flags & PF_W) == 0)
  3754                            ERROR (gettext ("\
  3755  section [%2zu] '%s' is writable in unwritable segment %d\n"),
  3756                                   cnt, section_name (ebl, cnt), pcnt);
  3757                        }
  3758                    }

This code tests each allocatable section (3694) and, among other things,
sets segments_flags (3742, 3751) -- permissions which corresponding ELF
segment should have (the segments are tested later against segments_flags).
However, only NOBITS sections contribute to ELF segment flags (3738).
This is possibly wrong: ".bss" section, despite the fact that it is
NOBITS (filled with zeroes upon startup), should be writable.

Here's a dumb patch which fixes the problem.

--- src/elflint.c-	2009-12-24 01:09:24 +0000
+++ src/elflint.c	2009-12-24 03:48:23 +0000
@@ -3794,7 +3794,10 @@ section [%2zu] '%s' has not type NOBITS 
 section [%2zu] '%s' is executable in nonexecutable segment %d\n"),
 				 cnt, section_name (ebl, cnt), pcnt);
 		      }
+		  }
 
+		if (shdr->sh_type != SHT_NOBITS || strcmp (scnname, ".bss") == 0)
+		  {
 		    if ((shdr->sh_flags & SHF_WRITE) != 0)
 		      {
 			segment_flags[pcnt] |= PF_W;
----------- следующая часть -----------
Было удалено вложение не в текстовом формате...
Имя     : отсутствует
Тип     : application/pgp-signature
Размер  : 198 байтов
Описание: отсутствует
Url     : <http://lists.altlinux.org/pipermail/devel/attachments/20091224/ae0dcf61/attachment-0001.bin>


Подробная информация о списке рассылки Devel