Showing posts with label PowerPC. Show all posts
Showing posts with label PowerPC. Show all posts

Monday, 12 June 2017

How to access global variables and TOC


int a = 2;
int b = 3;

int foo(void) {
  printf("a+b=%d",a + b);
  return 0;
}



foo:                                    # @foo
.Lfunc_begin0:
.Lfunc_gep0:
        addis r2, r12, .TOC.-.Lfunc_gep0@ha
        addi r2, r2, .TOC.-.Lfunc_gep0@l
.Lfunc_lep0:
        .localentry     foo, .Lfunc_lep0-.Lfunc_gep0
# BB#0:                                 # %entry
        mflr r0
        std r0, 16(r1) # save the link register to 16(r1), i.e stackFrame + 16`
        stdu r1, -96(r1)  # prolog
        addis r3, r2, .LC0@toc@ha
        addis r4, r2, .LC1@toc@ha
        addis r12, r2, .L.str@toc@ha
        ld r3, .LC0@toc@l(r3)
        ld r4, .LC1@toc@l(r4)
        lwz r3, 0(r3)
        lwz r4, 0(r4)
        add r3, r4, r3
        extsw r4, r3
        addi r3, r12, .L.str@toc@l
        bl printf
        nop  # may insert tls depending on the where is the callee definition.
        li r3, 0
        addi r1, r1, 96  # the reverse of the 3rd instr
        ld r0, 16(r1) # load the old link reigster address
        mtlr r0  # restore the link register value
        blr
        .long   0
        .quad   0
.Lfunc_end0:
        .size   foo, .Lfunc_end0-.Lfunc_begin0

Thursday, 18 May 2017

BE VS LE data layout (&sldwi)


Say you have a vector int a = {1,2,3,4}, vector int b = {5,6,7,8}
The lay out of a in the memory on a BE machine is pretty simply, just as it is:
Because BE means high word is save in low address ( but it is natural for array access)
Addr0x   0 1 2 3
BE:     [1,2,3,4]
On LE it is the opposite (high word is save in high address):
Addr0x   0 1 2 3
LE:     [4,3,2,1]

say you have xxsldwi(a,b,3)
On BE: it is just 0 1 2 3   4 5 6 7
                 [1,2,3,4] [5,6,7,8]
        The Result RT = {4,5,6,7}
        if you want to use vec_shuffle to implement xxsldwi, you have to pass in
        shuffle_vector((vector int)a, (vector int)b, 3,4,5,6), you can simply image BE
        is acess from Left to right.
        if you do  for (int i = 0; i < 4; i++)
  {
    printf("RT[%d]=%d\n\n",i, RT[i]);
  }
    it is just :
   c[0]=4

   c[1]=5

   c[2]=6

   c[3]=7
    
But On LE, for the same array a and b,
           it is just: 3 2 1 0   7 6 5 4
                      [4,3,2,1] [8,7,6,5]

        The Result RT = [1,8,7,6] in the register
        So no matter what, when xxsldwi(a,b,3), the word[3] of A, followed by
        word[0], word[1] and word[2] will be put into the result vector register
        if you want to use vec_shuffle to implement xxsldwi, you have to pass in
        shuffle_vector((vector int)a, (vector int)b,5,6,7,0), you can simply image LE
        is acess from right to left.
        if you do  for (int i = 0; i < 4; i++)
  {
    printf("RT[%d]=%d\n\n",i, RT[i]);
  }
    it is just :
   c[0]=6

   c[1]=7

   c[2]=8

   c[3]=1

Sunday, 29 January 2017

A quick introduction to simple functions


The PowerPC ABI is fairly complex, and will be covered in much greater detail in the next article. However, for functions which do not themselves call any functions and follow a few easy rules, the PowerPC ABI provides a greatly simplified function-call mechanism.
In order to qualify for the simplified ABI, your function must obey the following rules:
  • It must not call any other function.
  • It may only modify registers 3 through 12.
  • It may only modify condition register fields cr0, cr1, cr5, cr6, and cr7.
  • It must not alter the link register, unless it restores it before calling blr to return.
When functions are called, parameters are sent in registers, starting with register 3 and going through register 10, depending on the number of parameters. When the function returns, the return value must be stored in register 3.
So let's rewrite our maximum value program as a function, and call it from C.
The parameters we should pass are the pointer to the array as the first parameter (register 3), and the size of the array as the second parameter (register 4). Then, the maximum value will be placed into register 3 for the return value.

Monday, 23 January 2017

Global Varible access using GOT (Global Offset Table)



section:“.bss” contains the uninitialized global data,  while .data contains all the initialized data.
In general, the data segment of the executable contains initialized global/static variables and the BSS segment contains uninitialized global/static variables.

Figure 1. Memory access via the GOT


The GOT is private to each process, and the process must have write permissions to it. Conversely the library code is shared and the process should have only read and execute permissions on the code; it would be a serious security breach if the process could modify code.

------------------------------------------------------------------------------------------------
jtony@genoa:~/scrum/s7$ cat got.c
extern int i;
void test(void)
{
  i = 100;
}
------------------------------------------------------------------------------------------------

Above we create a simple shared library which refers to an external symbol. We do not know the address of this symbol at compile time, so we leave it for the dynamic linker to fix up at runtime. But we want our code to remain sharable, in case other processes want to use our code as well.
------------------------------------------------------------------------------------------------
$ clang -nostdlib  -shared -o got.so ./got.c
$  readelf --sections ./got.so
 Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .gnu.hash         GNU_HASH         0000000000000158  00000158
       0000000000000034  0000000000000000   A       2     0     8
  [ 9] .got              PROGBITS         0000000000020000  00010000
       0000000000000010  0000000000000008  WA       0     0     8
  [10] .comment          PROGBITS         0000000000000000  00010010
       00000000000000a5  0000000000000001  MS       0     0     1
------------------------------------------------------------------------------------------------

If we have a look at the readelf output above we can see that the .got section starts 0x20000 bytes past where library was loaded into memory.  Thus if the library were to be loaded into memory at address 0x6000000000000000 the .got would be at 0x6000000000020000,  The disassembly reveals just how we do this with the .got.

-----------------------------------
objdump --disassemble ./got.so 
Disassembly of section .text:
0000000000000290 <test>:
 290:   02 00 4c 3c     addis   r2,r12,2
 294:   70 7d 42 38     addi    r2,r2,32112
 298:   64 00 60 38     li      r3,100
 29c:   00 00 00 60     nop
 2a0:   08 80 82 e8     ld      r4,-32760(r2)
 2a4:   00 00 64 90     stw     r3,0(r4)
 2a8:   20 00 80 4e     blr
        ...

On ppc64le, the register r2 is known as the Toc pointer and always points to TOC base address.
The symbol .TOC. may be used to access the GOT or in TOC-relative addressing to other data constructs. The symbol may be offset by 0x8000 bytes, or another offset, from the start of the .got section. I think after line 290 – 294 (addis, addi) [need to confirm] , r2 will point to .GOT + 0x8000 (i.e 32768).  Then the ld on line 2a0, will set r4 point to .GOT + (32768 – 32760), i.e. r4 point to .GOT + 8

Table 4:  Relocation
-----------------------------------
jtony@genoa:~/scrum/s7$ readelf --relocs ./got.so
Relocation section '.rela.dyn' at offset 0x270 contains 1 entries:
000000020008  000300000026 R_PPC64_ADDR64    0000000000000000 i + 0
-----------------------------------


The relocation says "replace the value at offset 0x20008 with the memory location that symbol i is stored at".
We know that the .got starts at offset 0x20000 from the previous output. We have also seen how the code loads an address 0x8 past this ( using ld      r4,-32760(r2))  giving us an address of 0x20000 + 0x8 = 0x20008 ... the address which the relocation is for!
So before the program begins, the dynamic linker will have fixed up the relocation to ensure that the value of the memory at offset 0x20008 is the address of the global variable i!

Sunday, 22 January 2017

TOC section and its relation with GOT

ELF processor-specific supplements normally define a GOT ("Global Offset Table") section used to hold addresses for position independent code. Some ELF processor-specific supplements, including the 32-bit PowerPC Processor Supplement, define a small data section. The same register is sometimes used to address both the GOT and the small data section.
The 64-bit PowerOpen ABI defines a TOC ("Table of Contents") section. The TOC combines the functions of the GOT and the small data section.
This ABI uses the term TOC. The TOC section defined here is intended to be similar to that defined by the 64-bit PowerOpen ABI. The TOC section contains a conventional ELF GOT, and may optionally contain a small data area. The GOT and the small data area may be intermingled in the TOC section.
The TOC section is accessed via the dedicated TOC pointer register, r2. Accesses are normally made using the register indirect with immediate index mode supported by the 64-bit PowerPC processor, which limits a single TOC section to 65,536 bytes, enough for 8,192 GOT entries.
The value of the TOC pointer register is called the TOC base. The TOC base is typically the first address in the TOC plus 0x8000, thus permitting a full 64 Kbyte TOC.
A relocatable object file must have a single TOC section and a single TOC base. However, when the link editor combines relocatable object files to form a single executable or shared object, it may create multiple TOC sections. The link editor is responsible for deciding how to associate TOC sections with object files. Normally the link editor will only create multiple TOC sections if it has more than 65,536 bytes to store in a TOC.
All link editors which support this ABI must support a single TOC section, but support for multiple TOC sections is optional.
Each shared object will have a separate TOC or TOCs.
NoteNote
This ABI does not actually restrict the size of a TOC section. It is permissible to use a larger TOC section, if code uses a different addressing mode to access it. The AIX link editor, in particular, does not support multiple TOC sections, but instead inserts call out code at link time to support larger TOC sections.

Registers on PowerPC


The 64-bit PowerPC Architecture provides 32 general purpose registers, each 64 bits wide. In addition, the architecture provides 32 floating-point registers, each 64 bits wide, and several special purpose registers. All of the integer, special purpose, and floating-point registers are global to all functions in a running program. The following table shows how the registers are used.
r0        Volatile register used in function prologs
r1        Stack frame pointer
r2        TOC pointer
r3        Volatile parameter and return value register
r4-r10    Volatile registers used for function parameters
r11       Volatile register used in calls by pointer and as an
          environment pointer for languages which require one
r12       Volatile register used for exception handling and glink code
r13       Reserved for use as system thread ID
r14-r31   Nonvolatile registers used for local variables

f0        Volatile scratch register
f1-f4     Volatile floating point parameter and return value registers
f5-f13    Volatile floating point parameter registers
f14-f31   Nonvolatile registers

LR        Link register (volatile)
CTR       Loop counter register (volatile)
XER       Fixed point exception register (volatile)
FPSCR     Floating point status and control register (volatile)

CR0-CR1   Volatile condition code register fields
CR2-CR4   Nonvolatile condition code register fields
CR5-CR7   Volatile condition code register fields
On processors with the VMX feature.
v0-v1     Volatile scratch registers
v2-v13    Volatile vector parameters registers
v14-v19   Volatile scratch registers
v20-v31   Non-volatile registers
vrsave    Non-volatile 32-bit register
The existence of the VMX feature will be indicated in the AT_HWCAP auxiliary vector entry.
Registers r1, r14 through r31, and f14 through f31 are nonvolatile, which means that they preserve their values across function calls. Functions which use those registers must save the value before changing it, restoring it before the function returns. Register r2 is technically nonvolatile, but it is handled specially during function calls as described below: in some cases the calling function must restore its value after a function call.
Registers r0, r3 through r12, f0 through f13, and the special purpose registers LR, CTR, XER, and FPSCR are volatile, which means that they are not preserved across function calls. Furthermore, registers r0, r2, r11, and r12 may be modified by cross-module calls, so a function can not assume that the values of one of these registers is that placed there by the calling function.
The condition code register fields CR0, CR1, CR5, CR6, and CR7 are volatile. The condition code register fields CR2, CR3, and CR4 are nonvolatile; a function which modifies them must save and restore at least those fields of the CR. Languages that require "environment pointers" shall use r11 for that purpose.
The following registers have assigned roles in the standard calling sequence:
r1
The stack pointer (stored in r1) shall maintain quadword alignment. It shall always point to the lowest allocated valid stack frame, and grow toward low addresses. The contents of the word at that address always point to the previously allocated stack frame. If required, it can be decremented by the called function. See Section 3.5.13 for additional infromation. As discussed later in this chapter, the lowest valid stack address is 288 bytes less than the value in the stack pointer. The stack pointer must be atomically updated by a single instruction, thus avoiding any timing window in which an interrupt can occur with a partially updated stack.
r2
This register holds the TOC base. See Section 3.5.2 for additional information.
r3 through r10 and f1 through f13
These sets of volatile registers may be modified across function invocations and shall therefore be presumed by the calling function to be destroyed. They are used for passing parameters to the called function. See Section 3.2.3 for additional information. In addition, registers r3 and f1 through f4 are used to return values from the called function, as described in Section 3.2.4.
LR (Link Register)
This register shall contain the address to which a called function normally returns. LR is volatile across function calls.
Signals can interrupt processes (see signal (BA-OS) in the System V Interface Definition). Functions called during signal handling have no unusual restrictions on their use of registers. Moreover, if a signal handling function returns, the process resumes its original execution path with all registers restored to their original values. Thus, programs and compilers may freely use all registers above except those reserved for system use without the danger of signal handlers inadvertently changing their values.

Ref:
http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.7.html