Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# Libirecovery API Compatibility

## Overview

tr4mpass supports both **legacy** and **modern** versions of libirecovery through a centralized compatibility layer. This document explains the differences and how the project handles them.

## API Differences

### Issue 1: Device ID Field Change

| Version | Field | Type | Notes |
|---------|-------|------|-------|
| **Legacy** (pre-2024) | `info->pid` | `uint32_t` | Product ID (old terminology) |
| **Modern** (2024+) | `info->cpid` | `uint32_t` | Chip ID (new terminology) |

**What changed:** Modern libirecovery removed the `pid` field entirely, replacing it with `cpid`. Both represent the device identifier.

**Error you'd see:**
```
error: 'const struct irecv_device_info' has no member named 'pid'; did you mean 'cpid'?
```

### Issue 2: DFU Notify Flag Removal

| Version | Constant | Behavior |
|---------|----------|----------|
| **Legacy** (pre-2024) | `IRECV_SEND_OPT_DFU_NOTIFY_FINISH` | Explicit flag available |
| **Modern** (2024+) | *(removed)* | Must pass `0` as fallback |

**What changed:** The flag was removed as a named constant. Passing `0` has the same effect.

**Error you'd see:**
```
error: 'IRECV_SEND_OPT_DFU_NOTIFY_FINISH' undeclared (first use in this function)
```

## Solution: Compatibility Layer

### Header File: `include/compat/libirecovery_compat.h`

This header provides:

1. **`irecv_get_device_id(info)`** – Macro that returns the correct field based on available API
2. **`IRECV_DEVICE_ID_FIELD_NAME`** – String name for debug messages
3. **`IRECV_SEND_OPT_DFU_NOTIFY_FINISH`** – Defined as `0` if missing in modern API

### Usage Example

**Before (breaks on modern libirecovery):**
```c
if (!info || info->pid != APPLE_RECOVERY_PID) {
log_error("Device PID mismatch: 0x%04X", info->pid);
return -1;
}

irecv_send_file(client, path, IRECV_SEND_OPT_DFU_NOTIFY_FINISH);
```

**After (works on both):**
```c
#include "compat/libirecovery_compat.h"

uint32_t device_id = irecv_get_device_id(info);
if (!info || device_id != APPLE_RECOVERY_PID) {
log_error("Device %s mismatch: 0x%04X", IRECV_DEVICE_ID_FIELD_NAME, device_id);
return -1;
}

irecv_send_file(client, path, IRECV_SEND_OPT_DFU_NOTIFY_FINISH);
```

## Build Configuration

### Automatic Detection (Recommended)

The build system now auto-detects available libirecovery features:

```bash
make
```

The Makefile will:
1. Query `pkg-config` for libirecovery headers
2. Auto-detect whether `cpid` or `pid` is available
3. Check if `IRECV_SEND_OPT_DFU_NOTIFY_FINISH` exists
4. Apply appropriate compile flags automatically

**Build output example:**
```
libirecovery: cpid=yes dfu_notify=no
```

### Manual Override (If Needed)

If auto-detection fails or you need to force a specific API:

```bash
# Force legacy API (pid field)
make CFLAGS="-DHAVE_IRECV_DEVICE_INFO_PID"

# Force modern API (cpid field)
make CFLAGS="-DHAVE_IRECV_DEVICE_INFO_CPID"
```

## Files Modified

| File | Change |
|------|--------|
| `include/compat/libirecovery_compat.h` | **NEW** – Compatibility layer header |
| `src/bypass/path_b_identity.c` | Updated to use compat layer |
| `src/bypass/path_a_ramdisk.c` | Updated to use compat layer |
| `Makefile` | Added auto-detection logic |

## Tested Configurations

### ✅ Supported

- **Modern libirecovery** (2024+) – cpid field, no dfu_notify flag
- **Legacy libirecovery** (pre-2024) – pid field, dfu_notify flag available
- **Ubuntu 24.04 LTS** – Modern packages
- **Arch Linux** – Current repos (modern)
- **Older Debian/Ubuntu** – Legacy packages

### Installation Examples

**Modern (Ubuntu 24.04+):**
```bash
sudo apt install libirecovery-dev
```

**Legacy (Ubuntu 20.04, 22.04):**
```bash
sudo apt install libirecovery-dev # Older version
```

**Arch Linux:**
```bash
sudo pacman -S libirecovery
```

## Verifying Your Setup

### Check libirecovery Version

```bash
pkg-config --modversion libirecovery-1.0
```

### Check Available API

```bash
# Check for cpid (modern)
grep -q "cpid" /usr/include/libirecovery.h && echo "Modern API (cpid)" || echo "Legacy API (pid)"

# Check for dfu_notify flag
grep -q "IRECV_SEND_OPT_DFU_NOTIFY_FINISH" /usr/include/libirecovery.h && \
echo "DFU notify flag available" || echo "DFU notify flag missing"
```

## Troubleshooting

### Compilation fails with "no member named 'pid'"

This means your libirecovery is modern (2024+) but the compatibility layer isn't being used.

**Solution:**
```bash
make clean
make # Rebuilds with auto-detection
```

### Compilation fails with "undeclared 'IRECV_SEND_OPT_DFU_NOTIFY_FINISH'"

The flag is missing, which is expected on modern libirecovery. The compatibility layer should handle this.

**Solution:**
```bash
# Verify the compat header is included
grep -r "libirecovery_compat.h" src/
# Should see entries in path_a_ramdisk.c and path_b_identity.c
```

### Build succeeds but crashes at runtime

Rare, but can occur if the libirecovery runtime library differs from headers used at compile time.

**Solution:**
```bash
# Check actual installed library
ldconfig -p | grep libirecovery

# Verify pkg-config paths
pkg-config --cflags --libs libirecovery-1.0
```

## Contributing

If you encounter compatibility issues with a different libirecovery version:

1. **Report the version:** `pkg-config --modversion libirecovery-1.0`
2. **Share the error message**
3. **Dump the header:** `pkg-config --cflags libirecovery-1.0 | tr ' ' '\n'`

Open an issue on GitHub with this information.

## References

- [libirecovery GitHub](https://github.com/libimobiledevice/libirecovery)
- [libimobiledevice Project](https://libimobiledevice.org/)
35 changes: 32 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,35 @@ LDFLAGS += $(shell pkg-config --libs $(PKG_LIBS) 2>/dev/null)

$(foreach lib,$(PKG_LIBS),$(if $(shell pkg-config --exists $(lib) 2>/dev/null && echo ok),,$(warning Library $(lib) not found by pkg-config)))

# ================================================================
# Libirecovery API Compatibility Detection
# ================================================================
# Modern libirecovery (2024+) uses 'cpid' instead of 'pid'
# and removed IRECV_SEND_OPT_DFU_NOTIFY_FINISH constant.
# Detect what's available and set appropriate compile flags.

IRECV_HEADERS := $(shell pkg-config --cflags-only-I libirecovery-1.0 2>/dev/null)

# Check if cpid field is available (modern API)
ifeq ($(shell grep -q "cpid" $(IRECV_HEADERS)/libirecovery.h 2>/dev/null && echo found),found)
CFLAGS += -DHAVE_IRECV_DEVICE_INFO_CPID
IRECV_API = modern (cpid)
else
CFLAGS += -DHAVE_IRECV_DEVICE_INFO_PID
IRECV_API = legacy (pid)
endif

# Check if DFU notify flag is available
ifeq ($(shell grep -q "IRECV_SEND_OPT_DFU_NOTIFY_FINISH" $(IRECV_HEADERS)/libirecovery.h 2>/dev/null && echo found),found)
CFLAGS += -DHAVE_IRECV_SEND_OPT_DFU_NOTIFY_FINISH
IRECV_FLAG = available
else
IRECV_FLAG = missing (fallback: 0)
endif

# Print detected configuration at build time
$(info libirecovery API: $(IRECV_API), DFU notify flag: $(IRECV_FLAG))

# Auto-discover all C sources under src/
SRCS = $(shell find src -name '*.c')
OBJS = $(SRCS:.c=.o)
Expand Down Expand Up @@ -80,9 +109,9 @@ INT_SRCS = $(shell find tests/integration -name '*.c' 2>/dev/null)
# - src/main.c (has its own main())
# - src/exploit/* and src/device/usb_dfu.c (direct DFU/checkm8 h/w code)
MOCK_SUT_SRCS = $(shell find src -name '*.c' \
-not -path 'src/main.c' \
-not -path 'src/exploit/*' \
-not -path 'src/device/usb_dfu.c')
-not -path 'src/main.c' \
-not -path 'src/exploit/*' \
-not -path 'src/device/usb_dfu.c')
MOCK_ALL_SRCS = $(TEST_SECT) $(MOCK_SRCS) $(INT_SRCS) $(MOCK_SUT_SRCS)
MOCK_TARGET = tests/run_mock_tests

Expand Down
85 changes: 85 additions & 0 deletions include/compat/libirecovery_compat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* compat/libirecovery_compat.h -- Compatibility layer for libirecovery API.
*
* Handles differences between old and new libirecovery versions:
* - Old (pre-2024): uses info->pid field
* - New (2024+): uses info->cpid field, pid removed
*
* Also normalizes missing constants like IRECV_SEND_OPT_DFU_NOTIFY_FINISH
* which was removed in modern versions.
*
* Usage:
* #include "compat/libirecovery_compat.h"
*
* // Use this macro for device ID checks:
* uint32_t device_id = irecv_get_device_id(info);
*
* // Use this for the send file flag:
* irecv_send_file(client, path, IRECV_SEND_OPT_DFU_NOTIFY_FINISH);
*/

#ifndef LIBIRECOVERY_COMPAT_H
#define LIBIRECOVERY_COMPAT_H

#include <stdint.h>
#include <libirecovery.h>

#ifdef __cplusplus
extern "C" {
#endif

/* ============================================================================
* DEVICE ID FIELD COMPATIBILITY
* ============================================================================
* Modern libirecovery uses 'cpid' (chip ID), older versions used 'pid'
* (product ID). Both represent the device identifier.
*/

/*
* irecv_get_device_id -- Get the device ID from irecv_device_info.
*
* Abstracts the difference between old (pid) and new (cpid) fields.
* Returns 0 if info is NULL.
*/
static inline uint32_t irecv_get_device_id(const struct irecv_device_info *info)
{
if (!info)
return 0;

#ifdef HAVE_IRECV_DEVICE_INFO_CPID
/* Modern API: cpid field available */
return info->cpid;
#else
/* Legacy API: pid field available */
return info->pid;
#endif
}

/*
* IRECV_DEVICE_ID_FIELD_NAME -- String name of the field in use.
*
* Useful for debug/error messages to show which API is being used.
*/
#ifdef HAVE_IRECV_DEVICE_INFO_CPID
#define IRECV_DEVICE_ID_FIELD_NAME "cpid"
#else
#define IRECV_DEVICE_ID_FIELD_NAME "pid"
#endif

/* ============================================================================
* DFU NOTIFY FINISH FLAG COMPATIBILITY
* ============================================================================
* The IRECV_SEND_OPT_DFU_NOTIFY_FINISH flag was removed in modern libirecovery.
* We define it as 0 if missing, which is the safe fallback behavior.
*/

#ifndef IRECV_SEND_OPT_DFU_NOTIFY_FINISH
/* Modern API: flag not available, use 0 (safe fallback) */
#define IRECV_SEND_OPT_DFU_NOTIFY_FINISH 0
#endif

#ifdef __cplusplus
}
#endif

#endif /* LIBIRECOVERY_COMPAT_H */
4 changes: 4 additions & 0 deletions src/bypass/path_a_ramdisk.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "bypass/path_a_ramdisk_internal.h"
#include "device/device.h"
#include "util/log.h"
#include "compat/libirecovery_compat.h"

#define DEFAULT_SSH_PORT 2222
#define DEFAULT_TIMEOUT_SEC 120
Expand Down Expand Up @@ -140,6 +141,9 @@ send_staged_image(irecv_client_t client, const char *path,
}

log_info("path_a_ramdisk: sending %s (%s)", label, path);
/* Use compatibility layer for the DFU notify flag.
* Modern libirecovery (2024+) doesn't have the named constant,
* so the compatibility header defines it as 0 if missing. */
err = irecv_send_file(client, path, IRECV_SEND_OPT_DFU_NOTIFY_FINISH);
if (err != IRECV_E_SUCCESS) {
log_error("path_a_ramdisk: failed to send %s: %s",
Expand Down
13 changes: 9 additions & 4 deletions src/bypass/path_b_identity.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "device/usb_dfu.h"
#include "util/usb_helpers.h"
#include "util/log.h"
#include "compat/libirecovery_compat.h"

/* USB standard request codes (USB 2.0 spec table 9-4) */
#define USB_REQ_GET_DESCRIPTOR 0x06
Expand Down Expand Up @@ -188,6 +189,7 @@ int path_b_write_serial_irecovery(device_info_t *dev, const char *new_serial)
const struct irecv_device_info *info;
char cmd[DFU_SERIAL_MAX + 32];
int rc = -1;
uint32_t device_id;

if (!dev || !new_serial) {
log_error("[path_b_id] Invalid arguments to write_serial_irecovery");
Expand All @@ -207,11 +209,14 @@ int path_b_write_serial_irecovery(device_info_t *dev, const char *new_serial)
return -1;
}

/* Verify the device is actually in recovery (not DFU or normal) */
/* Verify the device is actually in recovery (not DFU or normal)
* Uses compatibility layer to handle both old (pid) and new (cpid) APIs */
info = irecv_get_device_info(client);
if (!info || info->pid != APPLE_RECOVERY_PID) {
log_error("[path_b_id] Device is not in recovery mode (pid=0x%04X)",
info ? (unsigned)info->pid : 0);
device_id = irecv_get_device_id(info);

if (!info || device_id != APPLE_RECOVERY_PID) {
log_error("[path_b_id] Device is not in recovery mode (%s=0x%04X)",
IRECV_DEVICE_ID_FIELD_NAME, device_id);
irecv_close(client);
return -1;
}
Expand Down