4

I'm trying to understand the concepts of the TOC and SDA in PowerPC.

From what I understand, they are basically pointers to tables of global values, when TOC is stored in r2, and SDA in r13.

But what are the difference between them? I compiled a simple ppc project, and I see that only r2 is used, but r13 isn't. Is it something that supposed to be said explicitly to use SDA?

Moreover, I see that all my global strings aren't actually accessed through r2 at all. It looks like the only use of r2 in my code is just to access stack variables. Does it make sense?

In addition, Where are they supposed to be initialized? I didn't find in my code any place that r2 is initialized, only used.

1 Answer 1

7

First, a bit of background on why these registers are needed. PPC is a RISC-like ISA, in that all instructions are of the same size (32 bits) and there is limited space for immediate values (usually 16 bits at most) that you would use for things like addresses. So how do you address more that 16 bits?

One option is to build addresses by 16-bit slices, e.g. something like:

 lis     r4,msg@ha     # load top 16 bits of &msg
 addi    r4,r4,msg@l   # add bottom 16 bits

Another is to use PIC (position-independent code) to address things relative to current address:

  bcl 20, 4*cr7+so, loc_picbase #branch and link to next location 
loc_picbase:
  mflr      r31 # load link register (lr) into r31. It will be equal to loc_picbase
  addis     r2, r31, (_NXArgc - loc_picbase)@ha
  stw       r28, (_NXArgc - loc_picbase)@l(r2)
  <and so on>

However, using either of these requires multiple instructions per data access and is not always feasible (e.g. first variant would require patching instructions if binary needs to be relocated which can't be done when running read-only code). So on some platforms they came up with the concept of TOC (Table of Contents), which is a fixed register having the same value for the whole module and from which all data is addressed using an offset. The register r2 was reserved for this purpose and AFAIK is supposed to be set by the OS loader. Since offsets in PPC instructions are limited to 16 bits (-32768 to +32767), you can address about 64K of data around r2 (which is usually placed in the middle of the data area for maximum coverage). If the module needs more data, offsets bigger than 16 bits are constructed in several steps, or multiple TOCs may be used (in this case different functions may use different TOCs).

In PPC64 ABI, the latter is achieved by so-called function descriptors which is pairs of pointers where one is the address of the function's code and the other is the TOC value expected by the function. Any time a function is called, the TOC is loaded from the descriptor before jumping to the function's code (unless the compiler knows that the target function uses the same TOC value, in which case the TOC reload can be skipped).

In embedded environment, there is usually no OS but a monolithic firmware, and this concept is applied to the whole firmware by using register r13 for SDA (Small Data Area) and, if necessary, r2 for an additional data region (SDA2 -Small Data Area 2). A so-called Zero data area (ZDA) may be also used for data placed near the address 0 (using special addressing modes from register r0).

Some compilers (notably Green Hills) support additional data pointers (r14, r15 etc.).

In embedded environment, the data registers (r2, r13 etc.) should be initialized in the startup code, shortly after reset, as the rest of the firmware usually expects them to have proper values.

P.S. usage of r2 depends on the environment. It's quite possible that the target environment you're compiling for does not use TOC so you don't see it used (stack pointer on PowerPC is r1, not r2).

Some references:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.