diff --git a/includes/form.inc b/includes/form.inc
index f1691ad..053c329 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -4290,10 +4290,14 @@ function theme_form_required_marker($variables) {
  * required. That is especially important for screenreader users to know
  * which field is required.
  *
+ * To associate the label with a different field, set the #label_for property
+ * to the ID of the desired field.
+ *
  * @param $variables
  *   An associative array containing:
  *   - element: An associative array containing the properties of the element.
- *     Properties used: #required, #title, #id, #value, #description.
+ *     Properties used: #required, #title, #id, #value, #description,
+ *     #label_for.
  *
  * @ingroup themeable
  */
@@ -4322,7 +4326,14 @@ function theme_form_element_label($variables) {
     $attributes['class'] = 'element-invisible';
   }
 
-  if (!empty($element['#id'])) {
+  // Use the element's ID as the default value of the "for" attribute (to
+  // associate the label with this form element), but allow this to be
+  // overridden in order to associate the label with a different form element
+  // instead.
+  if (!empty($element['#label_for'])) {
+    $attributes['for'] = $element['#label_for'];
+  }
+  elseif (!empty($element['#id'])) {
     $attributes['for'] = $element['#id'];
   }
 
diff --git a/modules/file/file.module b/modules/file/file.module
index fbf8b81..63758c4 100644
--- a/modules/file/file.module
+++ b/modules/file/file.module
@@ -357,10 +357,6 @@ function file_file_delete($file) {
  * support for a default value.
  */
 function file_managed_file_process($element, &$form_state, $form) {
-  // Append the '-upload' to the #id so the field label's 'for' attribute
-  // corresponds with the file element.
-  $original_id = $element['#id'];
-  $element['#id'] .= '-upload';
   $fid = isset($element['#value']['fid']) ? $element['#value']['fid'] : 0;
 
   // Set some default element properties.
@@ -370,7 +366,7 @@ function file_managed_file_process($element, &$form_state, $form) {
 
   $ajax_settings = array(
     'path' => 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'],
-    'wrapper' => $original_id . '-ajax-wrapper',
+    'wrapper' => $element['#id'] . '-ajax-wrapper',
     'effect' => 'fade',
     'progress' => array(
       'type' => $element['#progress_indicator'],
@@ -444,13 +440,26 @@ function file_managed_file_process($element, &$form_state, $form) {
   $element['upload'] = array(
     '#name' => 'files[' . implode('_', $element['#parents']) . ']',
     '#type' => 'file',
+    // This #title will not actually be used as the upload field's HTML label,
+    // since the theme function for upload fields never passes the element
+    // through theme('form_element'). Instead the parent element's #title is
+    // used as the label (see below). That is usually a more meaningful label
+    // anyway.
     '#title' => t('Choose a file'),
     '#title_display' => 'invisible',
+    // Set the ID manually so the desired field label can be associated with it
+    // below. Use the same method for setting the ID that the form API
+    // autogenerator does.
+    '#id' => drupal_html_id('edit-' . implode('-', array_merge($element['#parents'], array('upload')))),
     '#size' => $element['#size'],
     '#theme_wrappers' => array(),
     '#weight' => -10,
   );
 
+  // Indicate that $element['#title'] should be used as the HTML label for the
+  // file upload field.
+  $element['#label_for'] = $element['upload']['#id'];
+
   if ($fid && $element['#file']) {
     $element['filename'] = array(
       '#type' => 'markup',
@@ -465,13 +474,13 @@ function file_managed_file_process($element, &$form_state, $form) {
     $element['upload']['#attached']['js'] = array(
       array(
         'type' => 'setting',
-        'data' => array('file' => array('elements' => array('#' . $element['#id'] => $extension_list)))
+        'data' => array('file' => array('elements' => array('#' . $element['upload']['#id'] => $extension_list)))
       )
     );
   }
 
   // Prefix and suffix used for Ajax replacement.
-  $element['#prefix'] = '<div id="' . $original_id . '-ajax-wrapper">';
+  $element['#prefix'] = '<div id="' . $element['#id'] . '-ajax-wrapper">';
   $element['#suffix'] = '</div>';
 
   return $element;
