Skip to content

Commit

Permalink
Non-hardcoded _gp value support
Browse files Browse the repository at this point in the history
Closes #18
  • Loading branch information
AngheloAlf committed Aug 13, 2024
1 parent 46b191a commit 87179dd
Show file tree
Hide file tree
Showing 20 changed files with 414 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Useful for making sure a symbol from an static library is being linked.
- If a symbol assignment is emitted or not can be controlled with the same
conditional inclussion/exclussion mechanism used by the custom options.
- Add way to define a non hardcoded `_gp` symbol for a given segment.
- Used by defining the `gp_info` field on a segment.
- Can't be combined with the global `hardcoded_gp_value`.

### Changed

Expand Down
118 changes: 118 additions & 0 deletions docs/file_format/gp_info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Gp info


Check failure on line 3 in docs/file_format/gp_info.md

View workflow job for this annotation

GitHub Actions / Lint md files

Multiple consecutive blank lines [Expected: 1; Actual: 2]
Every attribute listed is optional unless explicitly stated.

## Table of contents

- [Gp info](#gp-info)
- [Table of contents](#table-of-contents)
- [`section`](#section)
- [Example](#example)
- [Valid values](#valid-values)
- [Default value](#default-value)
- [`offset`](#offset)
- [Example](#example-1)
- [Valid values](#valid-values-1)
- [Default value](#default-value-1)
- [`provide`](#provide)
- [Valid values](#valid-values-2)
- [Default value](#default-value-2)
- [`hidden`](#hidden)
- [Valid values](#valid-values-3)
- [Default value](#default-value-3)
- [`include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all`](#include_if_any-include_if_all-exclude_if_any-and-exclude_if_all)

## `section`

The `_gp` symbol will be emitted just before this section.

### Example

```yaml
segments:
- name: main
gp_info:
section: .sdata
```
### Valid values
Non-empty string.
### Default value
`.sdata`

## `offset`

An offset into the the section, allowing the `_gp` value to not point to the
start of the section, maximizing the available accessable range using the `$gp`
register.

### Example

```yaml
segments:
- name: main
gp_info:
offset: 0x8000
```

### Valid values

Integers.

### Default value

`0x7FF0`

## `provide`

If `provide` is enabled then the `_gp` symbol will only be set if it is
referenced by any linked code but is not defined in any object included in the
link.

See GNU LD documentation for
[`PROVIDE`](https://sourceware.org/binutils/docs/ld/PROVIDE.html).

This option can be combined with [`hidden`](#hidden). For more info see the GNU
LD documentation for
[`PROVIDE_HIDDEN`](https://sourceware.org/binutils/docs/ld/PROVIDE_005fHIDDEN.html).

### Valid values

Bool.

### Default value

`False`

## `hidden`

Allows defining the `_gp` symbol to be hidden and won't be exported.

On a more technical sense, the binding of the generated symbol on the elf will
be marked as `LOCAL` instead of `GLOBAL`.

See GNU LD documentation for
[`HIDDEN`](https://sourceware.org/binutils/docs/ld/HIDDEN.html).

This option can be combined with [`provide`](#provide). For more info see the
GNU LD documentation for
[`PROVIDE_HIDDEN`](https://sourceware.org/binutils/docs/ld/PROVIDE_005fHIDDEN.html).

### Valid values

Bool.

### Default value

`False`

## `include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all`

These fields allow to conditionally include or exclude a given segment depending
on the current [custom options](custom_options.md).

Their syntax is the same as their [`file`](file.md#include_if_any) counterparts.
40 changes: 29 additions & 11 deletions docs/file_format/segments.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,49 +38,51 @@ Every attribute listed is optional unless explicitly stated.
- [Example](#example-6)
- [Valid values](#valid-values-5)
- [Default value](#default-value-4)
- [`gp_info`](#gp_info)
- [Example](#example-7)
- [`include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all`](#include_if_any-include_if_all-exclude_if_any-and-exclude_if_all)
- [`alloc_sections`](#alloc_sections)
- [Example](#example-7)
- [Example](#example-8)
- [Valid values](#valid-values-6)
- [Default value](#default-value-5)
- [`noload_sections`](#noload_sections)
- [Example](#example-8)
- [Example](#example-9)
- [Valid values](#valid-values-7)
- [Default value](#default-value-6)
- [`subalign`](#subalign)
- [Example](#example-9)
- [Example](#example-10)
- [Valid values](#valid-values-8)
- [Default value](#default-value-7)
- [`segment_start_align`](#segment_start_align)
- [Example](#example-10)
- [Example](#example-11)
- [Valid values](#valid-values-9)
- [Default value](#default-value-8)
- [`segment_end_align`](#segment_end_align)
- [Example](#example-11)
- [Example](#example-12)
- [Valid values](#valid-values-10)
- [Default value](#default-value-9)
- [`section_start_align`](#section_start_align)
- [Example](#example-12)
- [Example](#example-13)
- [Valid values](#valid-values-11)
- [Default value](#default-value-10)
- [`section_end_align`](#section_end_align)
- [Example](#example-13)
- [Example](#example-14)
- [Valid values](#valid-values-12)
- [Default value](#default-value-11)
- [`sections_start_alignment`](#sections_start_alignment)
- [Example](#example-14)
- [Example](#example-15)
- [Valid values](#valid-values-13)
- [Default value](#default-value-12)
- [`sections_end_alignment`](#sections_end_alignment)
- [Example](#example-15)
- [Example](#example-16)
- [Valid values](#valid-values-14)
- [Default value](#default-value-13)
- [`wildcard_sections`](#wildcard_sections)
- [Example](#example-16)
- [Example](#example-17)
- [Valid values](#valid-values-15)
- [Default value](#default-value-14)
- [`fill_value`](#fill_value)
- [Example](#example-17)
- [Example](#example-18)
- [Valid values](#valid-values-16)
- [Default value](#default-value-15)

Expand Down Expand Up @@ -254,6 +256,22 @@ Any valid path.

Empty path.

## `gp_info`

Emits a `_gp` symbol for this segment.

For more information about this field see the dedicated
[`gp_info.md`](gp_info.md) document.

### Example

```yaml
segments:
- name: main
gp_info:
section: .sdata
```

## `include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all`

These fields allow to conditionally include or exclude a given segment depending
Expand Down
7 changes: 6 additions & 1 deletion docs/file_format/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,12 @@ Emits a `_gp` symbol with the specified value, essentially hardcoding the value.
This can be useful for decomp projects on the discovering step, but it would be
problematic on shiftable builds.

<!-- TODO: recommend the non-hardcoding alternative once it is implemented -->
It is strongly recommended to use the [`gp_info`](segments.md#gp_info) field of
the corresponding segment instead of hardcoding the `_gp` value, since this
will this value to shift properly when modifying the code of the builds.

Providing both [`gp_info`](segments.md#gp_info) and a `hardcoded_gp_value` is
not a valid combination and slinky will refuse to process the file.

### Example

Expand Down
4 changes: 2 additions & 2 deletions docs/file_format/symbol_assignments.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ Bool.

## `hidden`

Allows defining the symbol that will be hidden and won't be exported.
Defines the symbol but hides it, so it won't be exported.

On a more technical sense, the binding of the generated symbol on the elf will
be marked as `LOCAL` instead of `GLOBAL.`
be marked as `LOCAL` instead of `GLOBAL`.

See GNU LD documentation for
[`HIDDEN`](https://sourceware.org/binutils/docs/ld/HIDDEN.html).
Expand Down
9 changes: 8 additions & 1 deletion slinky/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* SPDX-FileCopyrightText: © 2024 decompals */
/* SPDX-License-Identifier: MIT */

use std::path::PathBuf;
use std::{borrow::Cow, path::PathBuf};

#[derive(Clone, Debug, PartialEq, Eq, Hash, thiserror::Error)]
pub enum SlinkyError {
Expand Down Expand Up @@ -46,4 +46,11 @@ pub enum SlinkyError {
path: PathBuf,
custom_option: String,
},

#[error("Field '{field_name}' refences the section '{section}', but that section is not present on segment '{segment}'")]
MissingSectionForSegment {
field_name: Cow<'static, str>,
section: Cow<'static, str>,
segment: Cow<'static, str>,
},
}
114 changes: 114 additions & 0 deletions slinky/src/gp_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/* SPDX-FileCopyrightText: © 2024 decompals */
/* SPDX-License-Identifier: MIT */

use serde::Deserialize;

use crate::{absent_nullable::AbsentNullable, traits::Serial, Settings, SlinkyError};

#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct GpInfo {
// The relative section to emit the `_gp` symbol
pub section: String,
// An offset into the small data section, used to maximize the range of small data.
pub offset: i32,

/// Signals if the `_gp` symbol should be wrapped in a `PROVIDE` statement.
/// Can be used with `hidden`.
pub provide: bool,
/// Signals if the `_gp` symbol should be wrapped in a `HIDDEN` statement.
/// Can be used with `provide`.
pub hidden: bool,

pub include_if_any: Vec<(String, String)>,
pub include_if_all: Vec<(String, String)>,
pub exclude_if_any: Vec<(String, String)>,
pub exclude_if_all: Vec<(String, String)>,
}

fn gp_info_default_section() -> String {
".sdata".to_string()
}

const fn gp_info_default_offset() -> i32 {
0x7FF0
}

const fn gp_info_default_provide() -> bool {
false
}

const fn gp_info_default_hidden() -> bool {
false
}

#[derive(Deserialize, PartialEq, Debug)]
#[serde(deny_unknown_fields)]
pub(crate) struct GpInfoSerial {
#[serde(default)]
pub section: AbsentNullable<String>,
#[serde(default)]
pub offset: AbsentNullable<i32>,

#[serde(default)]
pub provide: AbsentNullable<bool>,
#[serde(default)]
pub hidden: AbsentNullable<bool>,

#[serde(default)]
pub include_if_any: AbsentNullable<Vec<(String, String)>>,
#[serde(default)]
pub include_if_all: AbsentNullable<Vec<(String, String)>>,
#[serde(default)]
pub exclude_if_any: AbsentNullable<Vec<(String, String)>>,
#[serde(default)]
pub exclude_if_all: AbsentNullable<Vec<(String, String)>>,
}

impl Serial for GpInfoSerial {
type Output = GpInfo;

fn unserialize(self, _settings: &Settings) -> Result<Self::Output, SlinkyError> {
let section = {
let s = self
.section
.get_non_null("section", gp_info_default_section)?;
if s.is_empty() {
return Err(SlinkyError::EmptyValue {
name: "section".to_string(),
});
}
s
};

let offset = self.offset.get_non_null("offset", gp_info_default_offset)?;

let provide = self
.provide
.get_non_null("provide", gp_info_default_provide)?;
let hidden = self.hidden.get_non_null("hidden", gp_info_default_hidden)?;

let include_if_any = self
.include_if_any
.get_non_null_not_empty("include_if_any", Vec::new)?;
let include_if_all = self
.include_if_all
.get_non_null_not_empty("include_if_all", Vec::new)?;
let exclude_if_any = self
.exclude_if_any
.get_non_null_not_empty("exclude_if_any", Vec::new)?;
let exclude_if_all = self
.exclude_if_all
.get_non_null_not_empty("exclude_if_all", Vec::new)?;

Ok(Self::Output {
section,
offset,
provide,
hidden,
include_if_any,
include_if_all,
exclude_if_any,
exclude_if_all,
})
}
}
1 change: 1 addition & 0 deletions slinky/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod settings;

mod file_info;
mod file_kind;
mod gp_info;
mod required_symbol;
mod segment;
mod symbol_assignment;
Expand Down
Loading

0 comments on commit 87179dd

Please sign in to comment.