Toolchain notes on MIPS
2023-9-4 15:0:0 Author: maskray.me(查看原文) 阅读量:9 收藏

UNDER CONSTRUCTION

This article describes some notes about MIPS with a focus on the ELF object file format, GCC, binutils, and LLVM/Clang.

ISAs

Earlier ISAs, such as MIPS I, MIPS II, MIPS III, and MIPS IV, can be selected by gcc -march=mips[1-4]. MIPS V has no implementation and GCC doesn't support -march=mips5.

Successive versions are split into MIPS32 and MIPS64, which can be selected by -march=mips{32,64} and -march=mips{32,64}r{2,3,5,6}. -march=mips{32,64}r4 is not supported.

Release 6 is very different from prior releases.

  • -march=mips{32,64}r2 define __mips_isa_rev to 2.
  • -march=mips{32,64}r3 define __mips_isa_rev to 3.
  • -march=mips{32,64}r5 define __mips_isa_rev to 5.
  • -march=mips{32,64}r6 define __mips_isa_rev to 6.
  • Older processors don't define __mips_isa_rev.

The ISA revisions of GCC supported -march= values can be found at gcc/config/mips/mips-cpus.def.

The EF_MIPS_ARCH (0xf0000000) part of e_flags indicates the ISA.

When a 64-bit capable CPU is used with o32, assemblers set the EF_MIPS_32BITMODE flag in e_flags.

ABIs

o32

https://refspecs.linuxfoundation.org/elf/mipsabi.pdf

This is an ILP32 ABI designed for the 32-bit CPU MIPS R3000 that implements the MIPS I ISA. It is the original System V Processor Supplement ABI for MIPS.

In GCC, -mabi=32 selects this ABI, which is identified as ABI_32.

Assemblers set the EF_MIPS_ABI_O32 flag in e_flags.

There are some major flaws:

  • The calling convention provides just 4 integer registers for arguments (inadequate).
  • The ABI is committed to MIPS I and makes odd-numbered floating-point registers inaccessible.
  • Only two floating-point registers $12 and $14 hold arguments. The rest use integer registers, which are severely limited.

MIPS I has 32 floating-point registers. Two registers are paired for holding a double precision number.

  • For single-precision values, the even-numbered floating-point register holds the value.
  • For double-precision values, the even-numbered floating-point register holds the least significant 32 bits of the value and the odd-numbered floating-point register holds the most significant 32 bits of the value.

For o32, -mfp64 selects a variant that enables 64-bit floating-point registers. This requires at least -march=mips32r2 and GCC will emit .module fp=64; .module oddspreg.

n64

This is an LP64 ABI designed for 64-bit CPUs (MIPS III and newer).

In GCC, -mabi=64 selects this ABI, which is identified as ABI_64.

Assemblers don't set a particular bit in e_flags.

n32

This is an ILP32 ABI for 64-bit CPUs (MIPS III and newer), often named n32.

In GCC, -mabi=n32 selects this ABI, which is identified as ABI_N32. -march= values such as mips3, mips64 are compatible.

Assemblers set the EF_MIPS_ABI2 bit in e_flags.

o64

This is o32 extended for 64-bit CPUs. https://gcc.gnu.org/projects/mipso64-abi.html

In GCC, -mabi=o64 selects this ABI, which is identified as ABI_O64.

Assemblers set the EF_MIPS_ABI_O64 flag in e_flags.

EABI

Assemblers set the EF_MIPS_ABI_EABI32 flag in e_flags.


o32 and o64 are called TARGET_OLDABI in GCC. n32 and n64 are called TARGET_NEWABI in GCC.

gcc/config.gcc defines the default ABI (macro MIPS_ABI_DEFAULT) for different target triples.

In the old ABIs, the local label prefix for assembly is $, which is strange.


GCC emits a special section to indicate the ABI for GDB.

  • o32: .mdebug.abi32
  • n32: .mdebug.abiN32
  • n64: .mdebug.abi64
  • o64: mdebug.abiO64
  • 32-bit EABI: .mdebug.eabi32
  • 64-bit EABI: .mdebug.eabi64

gp register

Floating-point numbers

See NaN encodings.

IEEE 754-2008 says

A quiet NaN bit string should be encoded with the first bit (d1) of the trailing significand field T being 1.

-mnan=2008 instructs GCC to emit a .nan 2008 directive, which causes GNU assembler to set the EF_MIPS_NAN2008 bit in e_flags.

MIPS32/MIPS64 Release 6 defaults to -mnan=2008 while prior ISAs default to -mnan=legacy.

-modd-spreg for o32

Enable the use of odd-numbered single-precision floating-point registers for the o32 ABI. This option requires -march=mips32 or above.

I think the option should not be used with n32 or n64.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
% grep DT_MIPS_ include/elf/mips.h
#define DT_MIPS_RLD_VERSION 0x70000001
#define DT_MIPS_TIME_STAMP 0x70000002
#define DT_MIPS_ICHECKSUM 0x70000003
#define DT_MIPS_IVERSION 0x70000004
#define DT_MIPS_FLAGS 0x70000005
#define DT_MIPS_BASE_ADDRESS 0x70000006
#define DT_MIPS_MSYM 0x70000007
#define DT_MIPS_CONFLICT 0x70000008
#define DT_MIPS_LIBLIST 0x70000009
#define DT_MIPS_LOCAL_GOTNO 0x7000000a
#define DT_MIPS_CONFLICTNO 0x7000000b
#define DT_MIPS_LIBLISTNO 0x70000010
#define DT_MIPS_SYMTABNO 0x70000011
#define DT_MIPS_UNREFEXTNO 0x70000012
#define DT_MIPS_GOTSYM 0x70000013
#define DT_MIPS_HIPAGENO 0x70000014
#define DT_MIPS_RLD_MAP 0x70000016
#define DT_MIPS_DELTA_CLASS 0x70000017
/* Number of entries in DT_MIPS_DELTA_CLASS. */
#define DT_MIPS_DELTA_CLASS_NO 0x70000018
#define DT_MIPS_DELTA_INSTANCE 0x70000019
/* Number of entries in DT_MIPS_DELTA_INSTANCE. */
#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a
#define DT_MIPS_DELTA_RELOC 0x7000001b
/* Number of entries in DT_MIPS_DELTA_RELOC. */
#define DT_MIPS_DELTA_RELOC_NO 0x7000001c
#define DT_MIPS_DELTA_SYM 0x7000001d
/* Number of entries in DT_MIPS_DELTA_SYM. */
#define DT_MIPS_DELTA_SYM_NO 0x7000001e
#define DT_MIPS_DELTA_CLASSSYM 0x70000020
/* Number of entries in DT_MIPS_DELTA_CLASSSYM. */
#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021
#define DT_MIPS_CXX_FLAGS 0x70000022
#define DT_MIPS_PIXIE_INIT 0x70000023
#define DT_MIPS_SYMBOL_LIB 0x70000024
#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
#define DT_MIPS_LOCAL_GOTIDX 0x70000026
#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
#define DT_MIPS_OPTIONS 0x70000029
#define DT_MIPS_INTERFACE 0x7000002a
#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
#define DT_MIPS_INTERFACE_SIZE 0x7000002c
#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d
#define DT_MIPS_PERF_SUFFIX 0x7000002e
#define DT_MIPS_COMPACT_SIZE 0x7000002f
#define DT_MIPS_GP_VALUE 0x70000030
#define DT_MIPS_AUX_DYNAMIC 0x70000031
#define DT_MIPS_PLTGOT 0x70000032
#define DT_MIPS_RWPLT 0x70000034
#define DT_MIPS_RLD_MAP_REL 0x70000035
#define DT_MIPS_XHASH 0x70000036
/* Flags which may appear in a DT_MIPS_FLAGS entry. */

Among these dynamic tags, DT_MIPS_LOCAL_GOTNO, DT_MIPS_GOTSYM, DT_MIPS_SYMTABNO, and DT_MIPS_PLTGOT have significant uses in glibc rtld.

For executable output, DT_MIPS_RLD_MAP_REL holds the offset to the linker synthesized .rld_map. If DT_MIPS_RLD_MAP_REL is unavailable, glibc rtld looks for DT_MIPS_RLD_MAP, which is emitted for ET_EXEC executables.

Special sections

There are many special sections. Let me just quote a comment from binutils/readelf.c:process_mips_specific:

1

There are many sections from other vendors. Anyway, this remark sets up the mood.

.reginfo section

GNU assembler creates this section to hold Elf32_RegInfo object for o32 and n32 ABIs.

1
2
3
4
5
6
7
8
9
10
11


typedef struct
{

uint32_t ri_gprmask;

uint32_t ri_cprmask[4];

uint32_t ri_gp_value;
} Elf32_RegInfo;

.MIPS.options section

The n64 ABI uses .MIPS.options instead of .reginfo.

.MIPS.abiflags section

In 2014, [MIPS] Implement O32 FPXX, FP64 and FP64A ABI extensions introduced .MIPS.abiflags (type SHT_MIPS_ABIFLAGS) and PT_MIPS_ABIFLAGS.

1
2
3
4
5
6
7
8
9
A new implicitly generated section will be present on all new modules. The section contains a versioned data structure which represents essential information to allow a program loader to determine the requirements of the application. ELF e_flags currently contain some of this information but space is limited and e_flags are not available after loading an application.

The structure is versioned to allow for future extensions. The initial version is 0.

Register size fields record the maximum size register required for each class of registers
The floating point ABI is recorded using the same attribute values used for gnu_attribute 4. These are listed in Appendix B.
ASEs are represented by a bitmask of ASEs that have been used.
ISA extensions are represented as a single enumeration value.
The flags1 field includes a flag to record whether odd-numbered single-precision registers were enabled.

GNU ld has quite involved merging strategy for this section.

Relocations

The little-endian n64 ABI messed up the r_info field in relocations.

The n64 ABI can pack up to 3 relocations with the same offset into one record.

R_MIPS_PC64

This relocation type does not exist, but we can emulate a 64-bit PC-relative relocation with R_MIPS_PC32 + R_MIPS_64 + R_MIPS_NONE. LLVM implemented this relocation in https://reviews.llvm.org/D80390 while binutils doesn't support it yet.

1
2
3
4
5
.globl foo
foo:
.data
.quad foo-.
// R_MIPS_PC32 + R_MIPS_64 + R_MIPS_NONE

Paired relocations

TODO

Distributions

Here is an incomplete list.

FreeBSD 14 discontinued MIPS. MIPS code was removed starting since 2021-12.

Misc

E_MIPS_* macros are considered deprecated.


文章来源: https://maskray.me/blog/2023-09-04-toolchain-notes-on-mips
如有侵权请联系:admin#unsafe.sh