adimetrius / Herschel

Ground work for Component Pascal 64-bit compiler
BSD 2-Clause "Simplified" License
21 stars 1 forks source link

Elf: invalid relocations to not mapped memory #3

Closed X547 closed 3 years ago

X547 commented 3 years ago

This is https://github.com/adimetrius/Herschel/commit/cc4bead0534a6824bca446a8d906e7952b0d8ffd.

ELF segments of Albert sample:

Kind    File Offset File Size   Virt. Adr   Phys. Adr   Mem. Size   Align   Flags
load    00001000    00000080    00405000    00405000    00000080    00001000    write, read
load    00002000    00000058    00406000    00406000    00000058    00001000    exec, read
load    00003000    00000090    00407000    00407000    000000D0    00001000    write, read
load    00004000    000002D8    00408000    00408000    000002D8    00001000    write, read

Relocations:

Address     Old     Kind    Value   Addend
00405080    00000000    abs64   exit    0
00405078    00000000    abs64   puts    0

Note that address 00405080 do not point to any existing segment.

X547 commented 3 years ago

I attached Albert.odc that contains report of my ElfDecoder. Albert.zip

X547 commented 3 years ago

Disassembly utilities such as IDA Pro Free fails to recognize exit function because it is located outside of segment.

adimetrius commented 3 years ago

Well, this is by design, actually, but I'm open for criticism and suggestions.

The idea is that for every external function (imported from a library) the compiler expects a proxy in the special proxy table. So, when an external function is called, the actual address is loaded from the proxy table into a register, and a CALL [reg] is executed. The indexes into the proxy table are hard-coded by the compiler. It is the loader's responsibility to fill out the proxy table to provide for proper linking to external funcitons (be it the host OS or the BlackBox linker).

And it only seemed reasonable to me that the proxy table does not need to be present in the ELF file (and neither in the OCF file). Memory space is allocated for it (at the end of meta, segment 0 in your awesome decoder output), and then the OS's dynamic loader fills that out based on information provided in the DYNAMIC segment.

X547 commented 3 years ago

The problem is there are extra zero first entry in proxy table that is not included in segment size. Increasing segment that contains proxy table by 8 bytes will fix the issue. I am not sure what exact code should be adjusted in HrElf to properly fix issue. I added 1 to m.imps.

adimetrius commented 3 years ago

I see what you're saying. I think the line is in HrElf.DoAddOcf, and it's this one: AllocSeg(m.meta, AdAligned(m.ms) + (m.imps + m.exts) * addrSize[version], pageAligned); shoud be AllocSeg(m.meta, AdAligned(m.ms) + (1 + m.imps + m.exts) * addrSize[version], pageAligned);

If you confirm this fixes the issue, I'll put it into my modules.

X547 commented 3 years ago

AllocSeg(m.meta, AdAligned(m.ms) + (1 + m.imps + m.exts) * addrSize[version], pageAligned);

This will cause trap in size mismatch.

Place that I changed:

    PROCEDURE DoAddOcf (ocf: Files.File; VAR res: INTEGER);
        VAR s: OcfScanner; _, i: INTEGER; n: Name; meta, desc, code, imps: POINTER TO ARRAY OF BYTE; 
            prev, m: MModule; 
            c: ARRAY 3 OF LONGINT; 
    BEGIN
        s.ConnectTo(ocf, 0);    (* header and descriptor are in, module can be scanned *)
        IF ~s.ok THEN HALT(20)
        ELSE
            NEW(m); 
            m.ms := s.ms; m.ds := s.ds; m.cs:= s.cs; m.vs := s.vs; m.imps := s.imps + 1; (* zero entry in proxy table *) m.exts := s.exts; m.name := s.name$;
adimetrius commented 3 years ago

Hm... I tried my location of +1, and can't get a trap. Can you reproduce and send the trap you receive?

X547 commented 3 years ago

Hm... I tried my location of +1, and can't get a trap.

Sorry, trap was caused when inserting "+1" in one line above. Your change don't cause trap but it don't solve issue:

Reloc (Addend) 
Address Old Kind    Value   Addend
004050B0    00000000    abs64   Do6 0
004050A8    00000000    abs64   Do5 0
004050A0    00000000    abs64   Do4 0
00405098    00000000    abs64   Do3 0
00405090    00000000    abs64   Do2 0
00405088    00000000    abs64   Do1 0
00405080    00000000    abs64   exit    0
00405078    00000000    abs64   puts    0

Segment 0 (00405000, 000000B0, data) 
00405000  17 FD 07 FC 99 01 24 24 00 FD 10 80 70 40 00 00  ......$$....p@..
00405010  73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  s...............
00405020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00405030  00 62 75 74 20 6E 6F 74 20 73 69 6D 70 6C 65 72  .but not simpler
00405040  00 41 6C 62 65 72 74 20 61 73 6B 65 64 20 74 6F  .Albert asked to
00405050  20 6D 61 6B 65 20 69 74 20 61 73 20 73 69 6D 70   make it as simp
00405060  6C 65 20 61 73 20 70 6F 73 73 69 62 6C 65 2C 00  le as possible,.
00405070  00 00 00 00 00 00 00 00                          ........        
  __imp_puts        
00405070                          00 00 00 00 00 00 00 00          ........
  __imp_exit        
00405080  00 00 00 00 00 00 00 00                          ........        
  __imp_Do1     
00405080                          00 00 00 00 00 00 00 00          ........
  __imp_Do2     
00405090  00 00 00 00 00 00 00 00                          ........        
  __imp_Do3     
00405090                          00 00 00 00 00 00 00 00          ........
  __imp_Do4     
004050A0  00 00 00 00 00 00 00 00                          ........        
  __imp_Do5     
004050A0                          00 00 00 00 00 00 00 00          ........

Note that "__imp_Do6" is missing. My position fixes issue:

Reloc (Addend) 
Address Old Kind    Value   Addend
004050B0    00000000    abs64   Do6 0
004050A8    00000000    abs64   Do5 0
004050A0    00000000    abs64   Do4 0
00405098    00000000    abs64   Do3 0
00405090    00000000    abs64   Do2 0
00405088    00000000    abs64   Do1 0
00405080    00000000    abs64   exit    0
00405078    00000000    abs64   puts    0

Segment 0 (00405000, 000000B8, data) 
00405000  17 FD 07 FC 99 01 24 24 00 FD 10 80 70 40 00 00  ......$$....p@..
00405010  73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  s...............
00405020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00405030  00 62 75 74 20 6E 6F 74 20 73 69 6D 70 6C 65 72  .but not simpler
00405040  00 41 6C 62 65 72 74 20 61 73 6B 65 64 20 74 6F  .Albert asked to
00405050  20 6D 61 6B 65 20 69 74 20 61 73 20 73 69 6D 70   make it as simp
00405060  6C 65 20 61 73 20 70 6F 73 73 69 62 6C 65 2C 00  le as possible,.
00405070  00 00 00 00 00 00 00 00                          ........        
  __imp_puts        
00405070                          00 00 00 00 00 00 00 00          ........
  __imp_exit        
00405080  00 00 00 00 00 00 00 00                          ........        
  __imp_Do1     
00405080                          00 00 00 00 00 00 00 00          ........
  __imp_Do2     
00405090  00 00 00 00 00 00 00 00                          ........        
  __imp_Do3     
00405090                          00 00 00 00 00 00 00 00          ........
  __imp_Do4     
004050A0  00 00 00 00 00 00 00 00                          ........        
  __imp_Do5     
004050A0                          00 00 00 00 00 00 00 00          ........
  __imp_Do6     
004050B0  00 00 00 00 00 00 00 00                          ........        

In this code segment size is defined by LEN(meta) + LEN(imps):

                IF m.imps > 0 THEN NEW(imps, m.imps * addrSize[version]) END;
...
                    OutSeg(m.meta, meta, imps); 
    PROCEDURE OutSeg (IN s: SSegment; a: ARRAY OF BYTE; b: POINTER TO ARRAY OF BYTE);
    (** Output data in a and b (if any) into segment s *)
    BEGIN
        SkipTo(s.pos); ASSERT(s.pos = out.Pos());
        out.WriteBytes(a, 0, LEN(a)); IF b # NIL THEN out.WriteBytes(b^, 0, LEN(b)) END;
        ASSERT(out.Pos() = s.pos + s.len)
    END OutSeg;
X547 commented 3 years ago

Fixed In HrBinder.